bitsofcode Articles on frontend development and more. 2023-03-21T00:00:00Z https://bitsofco.de/ Ire Aderinokun ire@ireaderinokun.com When is :focus-visible visible? 2023-03-21T00:00:00Z https://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>&lt;input&gt;</code> or <code>&lt;textarea&gt;</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>&lt;button&gt;</code> and an <code>&lt;input&gt;</code>. For the <code>&lt;button&gt;</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>&lt;input&gt;</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>&lt;button&gt;</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 11ty 2023-02-22T00:00:00Z https://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">&amp;&amp;</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 &quot;image.png&quot;, &quot;Image description&quot; %} </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 &quot;css-grid&quot; %} </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>\&quot;</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 Cheatsheet 2021-10-25T00:00:00Z https://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/">&quot;User-centric Performance Metrics&quot;</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 &quot;visual completion&quot; 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>&lt;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>&lt;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>&lt;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>&lt;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>&lt;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>&lt;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>&lt;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>&lt;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>&lt;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>&lt;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>&lt;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:00Z https://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">&lt;!</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">&lt;</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">&lt;</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;</span>title</span><span class="token punctuation">></span></span>ire.eth<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>script</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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 2020 2020-12-14T00:00:00Z https://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 &quot;Core Web Vitals&quot; <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=&quot;module&quot;</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">&lt;</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">&lt;/</span>script</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre> <p>Browsers that support <code>type=&quot;module&quot;</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! (&amp; 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 React 2020-04-28T00:00:00Z https://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>&lt;Query&gt;</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">&lt;</span>View<span class="token operator">></span><br> <span class="token operator">&lt;</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">&lt;</span>Text<span class="token operator">></span>Loading<span class="token operator">&lt;</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">&lt;</span>Text<span class="token operator">></span>Error<span class="token operator">&lt;</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">&lt;</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">&lt;</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">&lt;</span><span class="token operator">/</span>Query<span class="token operator">></span><br> <span class="token operator">&lt;</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:00Z https://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 &quot;live images&quot; 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">&lt;</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">&amp;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">&lt;</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">&amp;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 2019 2019-11-18T00:00:00Z https://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> &amp; <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&amp;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>&lt;select&gt;</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>&lt;select&gt;</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&amp;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&amp;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&amp;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&amp;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&amp;feature=emb_title">Adaptive Loading</a>&quot;, 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) function 2019-10-28T00:00:00Z https://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&amp;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:00Z https://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>&amp;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>&amp;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, explained 2019-08-27T00:00:00Z https://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 Lighthouse 2019-05-28T00:00:00Z https://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:00Z https://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">&lt;</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">&lt;</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 devices 2019-02-12T00:00:00Z https://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">&lt;</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">&lt;</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">&lt;/</span>a</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</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">&lt;</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">&lt;</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">&lt;/</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>&lt;input&gt;</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">&lt;</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">&lt;</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">&lt;/</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>&lt;input&gt;</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">&lt;</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">&lt;</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">&lt;/</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 element 2019-02-05T00:00:00Z https://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>&lt;abbr&gt;</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">&lt;</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">&lt;/</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>&lt;abbr&gt;</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>&lt;abbr&gt;</code> would be would announced on first focus, but if re-navigating to an <code>&lt;abbr&gt;</code>, NVDA might not announce anything.</li> <li>While not a popular pairing, IE11 and NVDA would go into forms mode when an <code>&lt;abbr&gt;</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>&lt;abbr&gt;</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">&lt;</span>p</span><span class="token punctuation">></span></span>Cascading Style Sheets (<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>abbr</span><span class="token punctuation">></span></span>CSS<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>abbr</span><span class="token punctuation">></span></span>)<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre> <p>Everywhere else in the document, you can use the <code>&lt;abbr&gt;</code> element as normal. Since the abbreviation has already been defined earlier in the document, the limitations of the native <code>&lt;abbr&gt;</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">&lt;</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>abbr</span><span class="token punctuation">></span></span> is awesome.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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>&lt;abbr&gt;</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>&lt;abbr&gt;</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">&lt;</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">&lt;</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">&lt;</span>abbr</span><span class="token punctuation">></span></span>CSS<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>abbr</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>span</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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>&lt;a&gt;</code>, which links to the tooltip. I used an <code>&lt;a&gt;</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">&amp;&amp;</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>&lt;abbr&gt;</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>&lt;abbr&gt;</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> &lt;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> &lt;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">&lt;/abbr><br> &lt;/a><br> &lt;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">&lt;/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">&lt;</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">&lt;/</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>&lt;a&gt;</code> element here may not be the most semantic, as it is not really intended to be a link</strong>. I used the <code>&lt;a&gt;</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>&lt;button&gt;</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 lifecycle 2019-01-29T00:00:00Z https://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:00Z https://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 &quot;static&quot;.</p> <p>Before ES6 modules, we had CommonJS modules which used the <code>require()</code> syntax. These modules were &quot;dynamic&quot;, 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 &quot;side effects&quot;? <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 styles 2019-01-16T00:00:00Z https://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">&lt;</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">&lt;</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">&lt;</span>head</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;</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">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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">&lt;</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">&lt;</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">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;</span>p</span><span class="token punctuation">></span></span>Hola!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>blockquote</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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 &quot;content language&quot; <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>&lt;meta http-equiv=&quot;content-language&quot; content=&quot;en&quot;&gt;</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 Manifest 2018-12-25T00:00:00Z https://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>&lt;link&gt;</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">&lt;</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">></span></span>bitsofcode<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;</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">&lt;</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">&lt;</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">&lt;</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">&lt;</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">&lt;</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">&lt;</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">&lt;</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">&lt;</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">&lt;</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">&lt;/</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 DOM 2018-12-24T00:00:00Z https://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">&lt;!</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">&lt;</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">&lt;</span>head</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;</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">&lt;/</span>li</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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=&quot;en&quot;</p> </li> <li> <p>body</p> <ul> <li> <p>ul class=&quot;list&quot;</p> <ul> <li> <p>li class=&quot;list__item&quot;</p> <ul> <li>&quot;List item&quot;</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>&quot;List item one&quot;</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>&lt;li class="list__item">List item one&lt;/li><br>&lt;li class="list__item">List item two&lt;/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 &quot;virtual DOM&quot; 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=&quot;en&quot;</p> </li> <li> <p>body</p> <ul> <li> <p>ul class=&quot;list&quot;</p> <ul> <li> <p>li class=&quot;list__item&quot;</p> <ul> <li>&quot;List item&quot;</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/Base 2018-12-21T00:00:00Z https://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 &amp; 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 &amp; 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 mouse 2018-12-20T00:00:00Z https://bitsofco.de/making-abbr-work-for-touchscreen-keyboard-mouse/ <p>The <code>&lt;abbr&gt;</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>&lt;abbr&gt;</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">&lt;</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">&lt;/</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 &quot;CSS&quot; and a tooltip below showing &quot;Cascading Style Sheets&quot;" 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>&lt;abbr&gt;</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>&lt;abbr&gt;</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>&lt;abbr&gt;</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">&lt;</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">&lt;/</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>&lt;abbr&gt;</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>&lt;abbr&gt;</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 &quot;CSS&quot; and a tooltip below showing &quot;Cascading Style Sheets&quot;" 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 worker 2018-12-19T00:00:00Z https://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>&lt;img&gt;</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">&amp;&amp;</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 matches 2018-12-18T00:00:00Z https://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 &quot;grid&quot; 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 &quot;Hello&quot;, 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">&lt;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">&lt;/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>&lt;mark&gt;</code> element surrounding it.</p> <p>The <code>&lt;mark&gt;</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>&lt;h2><br> &lt;a href="/the-visibility-property-isnt-just-about-visibility/"><br> The visibility property isn’t just about visibility<br> &lt;/a><br>&lt;/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">&lt;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">&lt;/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>&lt;mark&gt;</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">&lt;</span>h2</span><span class="token punctuation">></span></span><br> &lt;a href=\"/the-<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>mark</span><span class="token punctuation">></span></span>visibility<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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">&lt;</span>mark</span><span class="token punctuation">></span></span>visibility<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>mark</span><span class="token punctuation">></span></span>/\"><br> The <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>mark</span><span class="token punctuation">></span></span>visibility<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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">&lt;</span>mark</span><span class="token punctuation">></span></span>visibility<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>mark</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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 elements 2018-12-17T00:00:00Z https://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">&lt;</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">&lt;/</span>style</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;</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">&lt;</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">&lt;/</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">&lt;</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">&lt;/</span>style</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</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">&lt;</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">&lt;/</span>style</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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">&lt;</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">&lt;/</span>style</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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">&lt;</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">&lt;</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">&lt;</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">&lt;/</span>span</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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>&lt;button&gt;</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">&lt;</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">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</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>&lt;span&gt;</code> is purely to label, whereas the <code>&lt;img&gt;</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-live 2018-12-14T00:00:00Z https://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>&lt;main&gt;</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">&lt;</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">&lt;!-- content will be updated with Javascript --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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=&quot;off&quot;</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>&lt;button&gt;</code> element which, on click, will update the content of the <code>&lt;div&gt;</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">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p>As you’ll see in the video below, since the <code>&lt;div&gt;</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=&quot;polite&quot;</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>&lt;div&gt;</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=&quot;assertive&quot;</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>&lt;div&gt;</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">&lt;</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">&lt;!-- content will be updated with Javascript --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;</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">&lt;</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">&lt;!-- content will be updated with Javascript --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;</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>&lt;body&gt;</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">&lt;</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">&lt;</span>header</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;!-- content will change --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;!-- content will change --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>footer</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>footer</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;!-- content will change --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;!-- content will change --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>footer</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>footer</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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>&lt;span&gt;</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">&lt;</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">&lt;</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">&lt;</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">&lt;/</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">&lt;/</span>span</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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 101 2018-12-13T00:00:00Z https://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 &gt; Cache Storage</li> <li><strong>Google Chrome</strong>: Application &gt; Cache &gt; 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 visibility 2018-12-12T00:00:00Z https://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>&lt;button&gt;</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">&lt;</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">&lt;/</span>span</span><span class="token punctuation">></span></span></code></pre> <p>It's gone! ---&gt; <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>&lt;div&gt;</code> elements, each with a width of 100px, with the middle <code>&lt;div&gt;</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">&lt;</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">&lt;/</span>style</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span>One<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span>Three<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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>&lt;div&gt;</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>&lt;button&gt;</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">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span><br>&lt;--- You can't touch it!</code></pre> <p>It's gone! ---&gt; <button style="visibility: hidden;" onclick="alert('Hello!')">I'm gone!</button> &lt;--- 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>&lt;button&gt;</code> element, with a nested <code>&lt;span&gt;</code> .</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>span</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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>&lt;button&gt;</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:00Z https://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:00Z https://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">&lt;!</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">&lt;</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">&lt;</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>title</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Hello, world!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>How are you?<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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>&lt;iframe&gt;</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>&lt;iframe&gt;</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>&lt;iframe&gt;</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">&lt;</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>&lt;input&gt;</code> element is actually made up of several smaller <code>&lt;div&gt;</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>&lt;input&gt;</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>&lt;iframe&gt;</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">&lt;</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">&lt;</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">&lt;/</span>a</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span></code></pre> <p>Note that we didn’t just use the <code>&lt;a&gt;</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>&lt;html&gt;</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>&lt;a&gt;</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> &lt;span aria-label="Twitter icon">&lt;/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 &quot;Follow-@ireaderinokun&quot;" 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>&lt;style&gt;</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 &quot;lite&quot; 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:00Z https://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 &amp; 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 &amp; 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 &amp; 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 &amp; 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 2018 2018-11-30T00:00:00Z https://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 &amp; 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>&lt;head&gt;</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>&lt;h1&gt;</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 &quot;opinionated&quot; 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>&lt;input&gt;</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>&lt;body&gt;</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 `&lt;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 attribute 2018-11-29T00:00:00Z https://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>&lt;a&gt;</code> elements with an <code>href</code> attribute</li> <li><code>&lt;link&gt;</code> elements with an <code>href</code> attribute</li> <li><code>&lt;button&gt;</code> elements</li> <li><code>&lt;input&gt;</code> elements that are not <code>type=&quot;hidden&quot;</code></li> <li><code>&lt;select&gt;</code> elements</li> <li><code>&lt;textarea&gt;</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">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span></code></pre> <p><button type="button">Click me to start, then press the &quot;tab&quot; 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">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span></code></pre> <p><button type="button">Click me to start, then press the &quot;tab&quot; 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">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</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>&lt;div&gt;</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">&lt;</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">&lt;/</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 &quot;tab&quot; 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">&lt;</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">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</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>&lt;div&gt;</code> or <code>&lt;section&gt;</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">&lt;</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">&lt;!-- Modal content --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</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>&lt;button&gt;</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">&lt;</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">&lt;/</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 variables 2018-11-28T00:00:00Z https://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 developers 2018-11-27T00:00:00Z https://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 &gt; Users &gt; ireaderinokun &gt; .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 . &amp;&amp; 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>&quot;Initial commit&quot;</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 &amp;&amp; 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 &amp; 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 &amp; 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 &amp; 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:00Z https://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>&lt;html&gt;</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">&lt;!</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">&lt;</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">&lt;</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>title</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Hello, world!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>How are you?<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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">&lt;!</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">&lt;</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">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre> <p>The document is missing a <code>&lt;head&gt;</code> and <code>&lt;body&gt;</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">&lt;!</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">&lt;</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">&lt;</span>head</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Hello, world!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre> <p>The DOM will include the <code>&lt;p&gt;</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 Cloudinary 2018-11-23T00:00:00Z https://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 screenshots 2018-11-22T00:00:00Z https://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 &quot;Can I Use css-grid? Data on support for the css-grid feature across the major browsers from caniuse.com.&quot;" 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">&lt;</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">&lt;</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">&lt;/</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">&lt;/</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 today 2018-11-21T00:00:00Z https://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>&lt;picture&gt;</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">&lt;</span>picture</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;</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">&lt;</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">&lt;/</span>picture</span><span class="token punctuation">></span></span></code></pre> <p>To provide an alternate image source, we use the <code>&lt;source&gt;</code> element within the <code>&lt;picture&gt;</code> element. The <code>&lt;source&gt;</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>&lt;source&gt;</code>s, a regular <code>&lt;img&gt;</code> element must also be provided as a fallback for browsers that do not support multiple file formats via the <code>&lt;picture&gt;</code> element.</p> Web workers vs Service workers vs Worklets 2018-11-20T00:00:00Z https://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>&lt;script&gt;</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, &amp; 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 loop 2018-11-19T00:00:00Z https://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">&lt;</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 2018 2018-11-16T00:00:00Z https://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>&lt;portal&gt;</code> element is similar to an <code>&lt;iframe&gt;</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>&lt;portal&gt;</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">&lt;</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">&lt;/</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&amp;t=0s&amp;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">&lt;</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:00Z https://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 &amp; 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 again 2018-11-14T00:00:00Z https://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, &amp; 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-auto 2018-10-09T00:00:00Z https://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 &quot;everything else&quot; -</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 &amp; 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">&lt;</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">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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 Extension 2018-05-23T00:00:00Z https://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; Works 2018-03-27T00:00:00Z https://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 &quot;rectangular box&quot; 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">&lt;</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">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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">&lt;</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">&lt;/</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">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;</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">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</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">&lt;</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">&lt;/</span>div</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">// => &lt;div class="outer">&lt;/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">&lt;/</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">&lt;</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">&lt;/</span>style</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</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 &quot;boxes&quot; 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 &quot;box&quot; 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 &quot;box&quot;. 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>&lt;select&gt;</code>, <code>&lt;input&gt;</code>, and <code>&lt;textarea&gt;</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>&lt;button&gt;</code> and <code>&lt;a&gt;</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 &quot;cards&quot; 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 &quot;card&quot; 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">&lt;</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">&lt;</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">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Footer stuff<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Footer stuff<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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 &quot;card&quot; within an <code>&lt;article&gt;</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">&lt;</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">&lt;</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">&lt;</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">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Footer stuff<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;</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">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Footer stuff<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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 Layout 2018-01-23T00:00:00Z https://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 &quot;days&quot; column</li> <li>The &quot;months&quot; 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">&lt;</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">&lt;</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">&lt;/</span>ul</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>ul</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;/</span>ul</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</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 &quot;phantom&quot; 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 &quot;Days&quot; 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 &quot;Months&quot; 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 punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>12<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>--week-width<span class="token punctuation">)</span> * 4<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>However, each month doesn’t span across exactly 4 even weeks. There is some overlap, with some months spanning across less or more. Because of this, I opted to manually set the column width for each month.</p> <pre class="language-css" tabindex="0"><code class="language-css"><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 punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--week-width<span class="token punctuation">)</span> * 4<span class="token punctuation">)</span> <span class="token comment">/* Jan */</span><br> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--week-width<span class="token punctuation">)</span> * 4<span class="token punctuation">)</span> <span class="token comment">/* Feb */</span><br> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--week-width<span class="token punctuation">)</span> * 4<span class="token punctuation">)</span> <span class="token comment">/* Mar */</span><br> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--week-width<span class="token punctuation">)</span> * 5<span class="token punctuation">)</span> <span class="token comment">/* Apr */</span><br> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--week-width<span class="token punctuation">)</span> * 4<span class="token punctuation">)</span> <span class="token comment">/* May */</span><br> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--week-width<span class="token punctuation">)</span> * 4<span class="token punctuation">)</span> <span class="token comment">/* Jun */</span><br> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--week-width<span class="token punctuation">)</span> * 5<span class="token punctuation">)</span> <span class="token comment">/* Jul */</span><br> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--week-width<span class="token punctuation">)</span> * 4<span class="token punctuation">)</span> <span class="token comment">/* Aug */</span><br> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--week-width<span class="token punctuation">)</span> * 4<span class="token punctuation">)</span> <span class="token comment">/* Sep */</span><br> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--week-width<span class="token punctuation">)</span> * 5<span class="token punctuation">)</span> <span class="token comment">/* Oct */</span><br> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--week-width<span class="token punctuation">)</span> * 4<span class="token punctuation">)</span> <span class="token comment">/* Nov */</span><br> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--week-width<span class="token punctuation">)</span> * 5<span class="token punctuation">)</span> <span class="token comment">/* Dec */</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Since I wasn’t relying on real data, I just copied what I saw on my actual GitHub graph at the time. Ideally, we would have a way to specify the widths of each column more programatically, but this is the way I decided to implement for the demonstrative purpose.</p> <h2 id="the-individual-squares" tabindex="-1">The Individual Squares <a class="header-anchor" href="https://bitsofco.de/github-contribution-graph-css-grid/">#</a></h2> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/MjewfziCFK-1128.avif 1128w"><source type="image/webp" srcset="https://bitsofco.de/img/MjewfziCFK-1128.webp 1128w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/MjewfziCFK-1128.png" width="1128" height="216"></picture></p> <p>The styles for the individual squares are mostly shared with the <code>.days</code> column, since the spacing and number of rows are meant to be identical.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.days,<br>.squares</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> <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 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>In addition to those shared styles, the individual squares are different in a number of ways.</p> <p>The first is the direction in which the squares are laid out. By default, items within a grid are laid out in the direction of the English language, i.e. left-to-right then top-to-bottom. However, the squares in the GitHub contribution graph are laid out differently, <strong>top-to-bottom then left-to-right</strong>.</p> <p>To lay our squares out in this way, we use the <code>grid-auto-flow</code> property.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.squares</span> <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 punctuation">}</span></code></pre> <p>Lastly, we want each column/square to be a set width (<code>var(--square-size)</code>). However, unlike the rows, we do not know or want to specify the exact amount of columns we have. So, instead of using the <code>grid-template-columns</code> property, we can use the <code>grid-auto-columns</code> property, setting it to the width we want each column to be, and letting the grid figure out how many columns are eventually drawn.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.squares</span> <span class="token punctuation">{</span><br> <span class="token property">grid-auto-columns</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 punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="the-finished-product" tabindex="-1">The Finished Product <a class="header-anchor" href="https://bitsofco.de/github-contribution-graph-css-grid/">#</a></h2> <p>You can see how everything works together in the CodePen below.</p> <p>See the Pen <a href="https://codepen.io/ire/pen/Legmwo/">GitHub Contribution Graph in CSS Grid Layout</a> by Ire Aderinokun (<a href="https://codepen.io/ire">@ire</a>) on <a href="https://codepen.io">CodePen</a>.</p> What’s New in HTML 5.2? 2018-01-08T00:00:00Z https://bitsofco.de/whats-new-in-html-5-2/ <p>Less than a month ago, HTML 5.2 became an official W3C Recommendation (REC). When a specification reaches the REC stage, this means that it has received the official endorsement of W3C Members and the Director, and that the W3C officially recommends it’s deployment by user agents, and it’s implementation by web page authors.</p> <p>By the REC stage, <a href="https://www.slideshare.net/rachelandrew/where-does-css-come-from/27?src=clipshare">anything new should have had at least 2 independent implementations</a>. This makes it a great time for us, as web page developers, to begin implementation of any new feature.</p> <p>In HTML 5.2, there were a number of additions and removals, all of which can be seen on the official <a href="https://www.w3.org/TR/html52/changes.html#changes">HTML 5.2 Changes</a> page. In this article, I’ll go over some of the changes I think will impact my development the most.</p> <h2 id="new-features" tabindex="-1">New Features <a class="header-anchor" href="https://bitsofco.de/whats-new-in-html-5-2/">#</a></h2> <h3 id="a-native-dialog-element" tabindex="-1">A native <code>&lt;dialog&gt;</code> element <a class="header-anchor" href="https://bitsofco.de/whats-new-in-html-5-2/">#</a></h3> <p>Of all the changes in HTML 5.2, I’m the most excited about the introduction of the <a href="https://www.w3.org/TR/html52/interactive-elements.html#elementdef-dialog"><code>&lt;dialog&gt;</code> element</a>, a native dialog box. Dialogs are incredibly prevalent on the web, yet every implementation is different in some way. Dialogs are also really difficult to do in a way that is accessible, resulting in most dialogs on the web being practically unusable for users who don’t navigate the web visually.</p> <p>The new <code>&lt;dialog&gt;</code> element aims to change this, providing a simple way to include a modal dialog without having to worry about many of the pitfalls. I will write a separate, detailed article about how this element works, but here are the basics.</p> <p>The dialog is created using a <code>&lt;dialog&gt;</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">&lt;</span>dialog</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Dialog Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Dialog content and other stuff will go here<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dialog</span><span class="token punctuation">></span></span></code></pre> <p>By default, the dialog is hidden from view (and from DOM access) unless the <code>open</code> attribute is applied.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dialog</span> <span class="token attr-name">open</span><span class="token punctuation">></span></span></code></pre> <p>The <code>open</code> attribute can be toggled by calling the <code>show()</code> and <code>close()</code> methods, which is available to any <code>HTMLDialogElement</code>.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</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>open<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Open Dialog<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</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>close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Close Dialog<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dialog</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>dialog<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">&lt;</span>h2</span><span class="token punctuation">></span></span>Dialog Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Dialog content and other stuff will go here<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dialog</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br><span class="token keyword">const</span> dialog <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"dialog"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"open"</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 punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> dialog<span class="token punctuation">.</span><span class="token function">show</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>document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"close"</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 punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> dialog<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><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre> <p>The <code>&lt;dialog&gt;</code> element already has support in Chrome, and is behind a flag in Firefox.</p> <p class="ciu_embed" data-feature="dialog" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false"> <picture> <source type="image/webp" srcset="https://caniuse.bitsofco.de/image/dialog.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/dialog.png"> <img src="https://caniuse.bitsofco.de/image/dialog.jpg" alt="Data on support for the dialog feature across the major browsers from caniuse.com"> </picture> </p> <h3 id="using-the-payment-request-api-from-iframes" tabindex="-1">Using the Payment Request API from iFrames <a class="header-anchor" href="https://bitsofco.de/whats-new-in-html-5-2/">#</a></h3> <p>The <a href="https://www.w3.org/TR/payment-request/">Payment Request API</a> is a native alternative to checkout forms. It aims to provide a standardised and consistent method of making payments on the web for users, by moving the handling of retrieving payment information to the browser, instead of individual checkout forms on each website.</p> <p>Before HTML 5.2, these payment requests couldn’t be made from iframes embedded in a document. This made it essentially impossible for third-party embedded payment solutions (e.g. Stripe, Paystack), to take advantage of this API, since their payment interface is typically handled within an iframe.</p> <p>HTML 5.2 introduced the <code>allowpaymentrequest</code> attribute which, when applied to an iframe, will allow it to use the Payment Request API while the user is on the hosting web page.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>iframe</span> <span class="token attr-name">allowpaymentrequest</span><span class="token punctuation">></span></span></code></pre> <h3 id="sizes-for-apple-icons" tabindex="-1">Sizes for Apple Icons <a class="header-anchor" href="https://bitsofco.de/whats-new-in-html-5-2/">#</a></h3> <p>To define web page icons, we use the <code>&lt;link rel=&quot;icon&quot;&gt;</code> element in the head of our document. To define different sizes of icons, we use the <code>sizes</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">&lt;</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">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>16x16<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>path/to/icon16.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">&lt;</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">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>path/to/icon32.png<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>This attribute, although purely advisory, allows user agents to decide which size of icon to use if multiple sizes are available, particularly since most devices have their own different &quot;optimal&quot; icon size.</p> <p>Before HTML 5.2, the <code>sizes</code> attribute was only valid if the link relationship was <code>icon</code>. However, Apple’s iOS devices do not support the <code>sizes</code> attribute. To get around this, Apple introduced a device-specific relationship, <code>appple-touch-icon</code> , which could be used to define the icon used on their devices.</p> <p>In HTML 5.2, the specification now allows the <code>sizes</code> attribute to be used if the relationship is <code>apple-touch-icon</code>, no longer <em>only</em> <code>icon</code>. This will allow us to provide different sizes of icons for different apple devices. Although, as far as I currently know, Apple devices still do not support the <code>sizes</code> attribute, this change will be useful for the future when they eventually do.</p> <h2 id="newly-valid-practices" tabindex="-1">Newly Valid Practices <a class="header-anchor" href="https://bitsofco.de/whats-new-in-html-5-2/">#</a></h2> <p>In addition to new features, HTML 5.2 has validated some practices in writing HTML that were previously invalid.</p> <h3 id="multiple-main-elements" tabindex="-1">Multiple <code>&lt;main&gt;</code> elements <a class="header-anchor" href="https://bitsofco.de/whats-new-in-html-5-2/">#</a></h3> <p>The <code>&lt;main&gt;</code> element represents the main content of a web page. While content that is repeated across multiple pages can be placed in headers, sections, or any other element, the <code>&lt;main&gt;</code> element is reserved for the content that is specific and unique to the particular page. Consequently, before HTML 5.2, the <code>&lt;main&gt;</code> element had to be unique within the DOM for the page to be valid.</p> <p>With the prevalence of Single Page Applications, however, sticking to this rule could be difficult. There may be cases in which there are multiple <code>&lt;main&gt;</code> elements in the DOM, but only one being shown to the user at any given time.</p> <p>With HTML 5.2, we can now have multiple <code>&lt;main&gt;</code> elements in our markup, as long as only one is visible to the user at any given time. Any extra elements must be hidden using the <code>hidden</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">&lt;</span>main</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span></code></pre> <p>As we know, there are <a href="https://bitsofco.de/hiding-elements-with-css">several ways to hide an element with CSS</a>. However, any extra <code>&lt;main&gt;</code> elements must be hidden using the <code>hidden</code> attribute. Any other method for hiding the element, e.g. <code>display: none;</code> or <code>visibility: hidden;</code> will not be valid.</p> <h3 id="styles-in-the-body" tabindex="-1">Styles in the <code>&lt;body&gt;</code> <a class="header-anchor" href="https://bitsofco.de/whats-new-in-html-5-2/">#</a></h3> <p>Typically, inline CSS defined using the <code>&lt;style&gt;</code> element is placed within the <code>&lt;head&gt;</code> of the HTML document. With the increase in component-ized development, developers have seen the benefit in writing and placing styles along with the html element they are relevant for.</p> <p>In HTML 5.2, defining an inline <code>&lt;style&gt;</code> block anywhere within the <code>&lt;body&gt;</code> of an HTML document is now valid. This means that we can have styles closer to where they are used.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>I’m cornflowerblue!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">p</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> cornflowerblue<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">&lt;/</span>style</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>I’m cornflowerblue!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span></code></pre> <p>However, it is still noted that <strong>styles should preferably be placed in the <code>&lt;head&gt;</code>, for performance reasons</strong>. According the the <a href="https://www.w3.org/TR/html52/document-metadata.html#elementdef-style">specification</a>,</p> <blockquote> <p>A style element should preferably be used in the head of the document. The use of style in the body of the document may cause restyling, trigger layout and/or cause repainting, and hence, should be used with care.</p> </blockquote> <p>It should also be noted that, as shown in the example, the styles are not scoped. An inline style defined later in the HTML document will still apply to elements defined before it, which is why it may trigger repainting.</p> <h3 id="headings-in-a-legend" tabindex="-1">Headings in a <code>&lt;legend&gt;</code> <a class="header-anchor" href="https://bitsofco.de/whats-new-in-html-5-2/">#</a></h3> <p>In forms, the <code>&lt;legend&gt;</code> element represents a caption for the form fields within a <code>&lt;fieldset&gt;</code>. Before HTML 5.2, the content of a legend had to be plain text. Now, we can include heading elements.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>fieldset</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>legend</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Basic Information<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>legend</span><span class="token punctuation">></span></span><br> <span class="token comment">&lt;!-- Form fields for basic information --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>fieldset</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>fieldset</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>legend</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Contact Information<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>legend</span><span class="token punctuation">></span></span><br> <span class="token comment">&lt;!-- Form fields for contact information --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>fieldset</span><span class="token punctuation">></span></span></code></pre> <p>This is really useful for when we want to use the <code>fieldset</code> element to group different sections of a form. In cases like this, it makes perfect sense to use headings, which would make these form sections more easily navigable for users relying on a document outline.</p> <h2 id="removed-features" tabindex="-1">Removed Features <a class="header-anchor" href="https://bitsofco.de/whats-new-in-html-5-2/">#</a></h2> <p>In HTML 5.2, a few elements were removed, namely:</p> <ul> <li><code>keygen</code>: Used to help generating public keys for forms</li> <li><code>menu</code> and <code>menuitem</code>: Used to create navigation or context menus</li> </ul> <h2 id="newly-invalid-practices" tabindex="-1">Newly Invalid Practices <a class="header-anchor" href="https://bitsofco.de/whats-new-in-html-5-2/">#</a></h2> <p>Finally, some development practices have been made invalid.</p> <h3 id="no-inline-floated-or-blocked-children-of-p" tabindex="-1">No inline, floated, or blocked children of <code>&lt;p&gt;</code> <a class="header-anchor" href="https://bitsofco.de/whats-new-in-html-5-2/">#</a></h3> <p>In HTML 5.2, the only valid children of a <code>&lt;p&gt;</code> element should be phrasing content. This means that the following types of elements should no longer be nested within a paragraph:</p> <ul> <li>Inline blocks</li> <li>Inline tables</li> <li>Floated and positioned positioned blocks</li> </ul> <h3 id="no-strict-doctypes" tabindex="-1">No Strict Doctypes <a class="header-anchor" href="https://bitsofco.de/whats-new-in-html-5-2/">#</a></h3> <p>Finally, we can say goodbye to the following strict document types:</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token doctype"><span class="token punctuation">&lt;!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">HTML</span> <span class="token name">PUBLIC</span> <span class="token string">"-//W3C//DTD HTML 4.01//EN"</span> <span class="token string">"https://www.w3.org/TR/html4/strict.dtd"</span><span class="token punctuation">></span></span></code></pre> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token doctype"><span class="token punctuation">&lt;!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span> <span class="token name">PUBLIC</span> <span class="token string">"-//W3C//DTD XHTML 1.0 Strict//EN"</span> <span class="token string">"https://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"</span><span class="token punctuation">></span></span></code></pre> Localisation and Translation on the Web 2017-11-23T00:00:00Z https://bitsofco.de/localisation-and-translation/ <p>Coming from the English-speaking world, it can be easy to maintain the bubble that is the English-speaking World Wide Web. But in fact, <a href="https://qz.com/96054/english-is-no-longer-the-language-of-the-web/">more than half of web pages are written in languages other than English</a>.</p> <p>Since starting work at <a href="https://eyeo.com">eyeo</a>, I’ve had to think a lot more about localisation and translations because most of our websites are translated into several languages, something I previously didn’t have to really consider before. Once you decide to translate a web page, there are many things to take into account, and a lot of them I've found are useful even if your website is written in only one language.</p> <h2 id="what-language-is-the-website" tabindex="-1">What Language is the Website? <a class="header-anchor" href="https://bitsofco.de/localisation-and-translation/">#</a></h2> <p>One of the first things we need to think about is what language the HTML document (or elements within) are written in. Using the <code>lang</code> attribute, we can let browsers know what language the web page is written in.</p> <p>Typically, we would add this attribute to the root element of the document, which is in most cases the HTML element.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>Adding this attribute to the root element is really important, particularly for users who’s main language used with their machine is not the same as the web page’s language. For example, a French-speaking user who visits this blog.</p> <p>In the absence of the <code>lang</code> attribute, the browser will assume the web page is written in the user’s default language, which can lead to some strange results. Here’s an example of a screen reader reading an English web page in a French accent due to a missing <code>lang</code> attribute -</p> <p><a href="https://vid.me/e/MRMw?stats=1"><picture><source type="image/avif" srcset="https://bitsofco.de/img/1eq-syNOgi-1278.avif 1278w"><source type="image/webp" srcset="https://bitsofco.de/img/1eq-syNOgi-1278.webp 1278w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/1eq-syNOgi-1278.png" width="1278" height="864"></picture></a></p> <p>The <code>lang</code> attribute is one of the global HTML attributes which allows it to be applied to any HTML element. This means that we can specify different sections of our web page as being written in different languages. This can be really useful, for example, if you are writing an article that references a text in a different language, <strong>comme ça, par exemple</strong>.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Localisation and Translation on the Web<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span><br> This can be really useful, for example, if you are writing an article that references a text in a different language, <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</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>comme ça, par exemple<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span></code></pre> <h2 id="specifying-language-for-external-pages" tabindex="-1">Specifying Language for External Pages <a class="header-anchor" href="https://bitsofco.de/localisation-and-translation/">#</a></h2> <p>When using the <code>lang</code> attribute, we can tell user agents what language the content on the current webpage is, but what about if we need to link to an external page/resource?</p> <p>We can specify the language of an externally linked resource using the <code>hreflang</code> attribute. As it’s name implies, it sets the language of a resource linked to via the <code>href</code> attribute, and can thus only be applied to elements that have this attribute, i.e. the <code>&lt;a&gt;</code>, <code>&lt;link&gt;</code>, and <code>&lt;area&gt;</code> elements.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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://adblockplus.org/ar/<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ar<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>adblockplus.org (Arabic)<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span></code></pre> <h2 id="controlling-translations" tabindex="-1">Controlling Translations <a class="header-anchor" href="https://bitsofco.de/localisation-and-translation/">#</a></h2> <p>In some cases, we may want a section of the web page to always be displayed in a certain language, never translated. This is the idea behind the new HTML5.1 <code>translate</code> attribute.</p> <p>The <code>translate</code> attribute can accept one of two values:</p> <ul> <li><code>yes</code>: The element’s contents should be translated</li> <li><code>no</code>: The element’s contents should not be translated</li> </ul> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>de<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">&lt;</span>p</span><span class="token punctuation">></span></span>Übersetze mich!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- Translate me! --></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">translate</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Übersetze mich nicht<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- Do not translate me --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre> <p>Not currently supported</p> <p>Unfortunately, this attribute is <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/translate#Browser_compatibility">not currently supported by any browser</a>. However, it's effect can be simulated by using the <code>.notranslate</code> class, which is respected by Google’s Web Page Translator. Take, for example, the following two paragraphs:</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>de<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">&lt;</span>p</span><span class="token punctuation">></span></span>Übersetze mich!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- Translate me! --></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>notranslate<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Übersetze mich nicht<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- Do not translate me --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre> <p>If this page is translated to another language, only the first paragraph will be translated.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/0F9aqScgYo-1548.avif 1548w"><source type="image/webp" srcset="https://bitsofco.de/img/0F9aqScgYo-1548.webp 1548w"><img alt="First paragraph translated, second paragraph not translated" loading="lazy" decoding="async" src="https://bitsofco.de/img/0F9aqScgYo-1548.png" width="1548" height="943"></picture></p> <h2 id="text-direction" tabindex="-1">Text Direction <a class="header-anchor" href="https://bitsofco.de/localisation-and-translation/">#</a></h2> <p>In many languages, the direction in which text is written is not left-to-right like it is in English. In languages such as Arabic, text is written (and read) from right-to-left.</p> <p>To change the direction in which text is written, we can use the <code>dir</code> attribute, which accepts one of three values:</p> <ul> <li><code>ltr</code>: Left to Right</li> <li><code>rtl</code>: Right to Left</li> <li><code>auto</code>: Allow the user agent to decide which direction based on the text content</li> </ul> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>ar<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>rtl<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>Based on this root direction, most browsers will apply the corresponding CSS styles to switch the direction in which text is displayed, using the <code>direction</code> property.</p> <p>The CSS <code>direction</code> property accepts one of two values - <code>ltr</code> or <code>rtl</code>.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">html[dir="rtl"]</span> <span class="token punctuation">{</span><br> <span class="token property">direction</span><span class="token punctuation">:</span> rtl<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>This property works in the same way as the <code>text-align</code> property. It doesn’t re-order the words in any way, it just aligns the text in the appropriate direction.</p> <p>Other relevant CSS properties for controlling text direction include:</p> <ul> <li><code>writing-mode</code>: This determines if text is laid out horizontally or vertically and the direction (<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode">See MDN</a>)</li> <li><code>text-orientation</code>: This determines the orientation of each character (<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/text-orientation">See MDN</a>)</li> </ul> <h2 id="alternates" tabindex="-1">Alternates <a class="header-anchor" href="https://bitsofco.de/localisation-and-translation/">#</a></h2> <p>For most sites that are translated into different languages, there are separate pages for each language. For example, there might be several versions of the homepage -</p> <ul> <li><code>https://adblockplus.org/en/</code> for the English version</li> <li><code>https://adblockplus.org/ar/</code> for the Arabic version</li> <li>...</li> </ul> <p>In order for user agents to know of all these separate pages and classify them correctly as the same page, just translated into different languages, we can use the <code>&lt;link&gt;</code> element, with the <code>alternate</code> relationship type. In the document <code>&lt;head&gt;</code> we can write out all the alternate versions for 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">&lt;</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">&lt;</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>alternate<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>https://adblockplus.org/ar<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ar<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> ...<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre> <p>Note that we use the <code>hreflang</code> attribute in combination with the <code>alternate</code> type to set the language each alternate page is in.</p> <h3 id="alternates-for-social-media" tabindex="-1">Alternates for Social Media <a class="header-anchor" href="https://bitsofco.de/localisation-and-translation/">#</a></h3> <p>When a link to a web page is shared, it’s language is typically determined from the <code>og:locale</code> meta tag.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>og:locale<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en_US<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>If there are multiple available locales, we can specify this using the <code>og:locale:alternate</code> meta tag.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">property</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>og:locale:alternate<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ar_AR<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <h2 id="left-right-start-end" tabindex="-1">Left, Right, Start, End <a class="header-anchor" href="https://bitsofco.de/localisation-and-translation/">#</a></h2> <p>Because most of the web was originally written with only English in mind, a lot of CSS was written with the mindset that the start of a line is the left, and the end of the line is the right. But as the web becomes more internationally aware, things are changing.</p> <p>For example, with Flexbox, the default “left” side of the box is called the “start”, because this can be on any of the four sides of the box itself. A lot of new CSS properties are starting to work this way, for example the new <code>margin-inline-start</code> property.</p> <p>The <code>margin-inline-start</code> property corresponds to the inline “start” margin of an element, and can be equal to any of the four sides of the element depending on the direction of the document. For example, if the direction on an element is right-to-left, then the start margin will be equivalent to the right margin.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">span</span> <span class="token punctuation">{</span><br> <span class="token property">direction</span><span class="token punctuation">:</span> rtl<span class="token punctuation">;</span><br> <span class="token property">margin-inline-start</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span> <span class="token comment">/* Equivalent to margin-right */</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/tvOQQ6AzjA-1270.avif 1270w"><source type="image/webp" srcset="https://bitsofco.de/img/tvOQQ6AzjA-1270.webp 1270w"><img alt="Right Margin on a span element" loading="lazy" decoding="async" src="https://bitsofco.de/img/tvOQQ6AzjA-1270.png" width="1270" height="338"></picture></p> <p>Similarly, if the <code>writing-mode</code> of an element is set as vertical and left-to-right, then the start margin will be equivalent to the top margin.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">span</span> <span class="token punctuation">{</span><br> <span class="token property">writing-mode</span><span class="token punctuation">:</span> vertical-lr<span class="token punctuation">;</span><br> <span class="token property">margin-inline-start</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span> <span class="token comment">/* Equivalent to margin-top */</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/FjSOKCCB05-1278.avif 1278w"><source type="image/webp" srcset="https://bitsofco.de/img/FjSOKCCB05-1278.webp 1278w"><img alt="Top Margin on a span element" loading="lazy" decoding="async" src="https://bitsofco.de/img/FjSOKCCB05-1278.png" width="1278" height="550"></picture></p> <p>There are other properties that work in this way, for example the corresponding <code>margin-inline-end</code>, which works similarly to <code>margin-inline-start</code>, but applies to the end of the element. Taking the first example from above, if the direction on an element in right-to-left, then the end margin will be equivalent to the left margin.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">span</span> <span class="token punctuation">{</span><br> <span class="token property">direction</span><span class="token punctuation">:</span> rtl<span class="token punctuation">;</span><br> <span class="token property">margin-inline-end</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span> <span class="token comment">/* Equivalent to margin-left */</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/BFTOp40-dV-1274.avif 1274w"><source type="image/webp" srcset="https://bitsofco.de/img/BFTOp40-dV-1274.webp 1274w"><img alt="Left Margin on a span element" loading="lazy" decoding="async" src="https://bitsofco.de/img/BFTOp40-dV-1274.png" width="1274" height="342"></picture></p> <hr> <p>There are so many more considerations that are involved when creating a localised website, particularly one that accepts user input. Feel free to share any tips you have come across in the comments below.</p> Web Push Notifications with Firebase (Video Series) 2017-09-18T00:00:00Z https://bitsofco.de/web-push-notifications-with-firebase/ <p>When I attended <a href="https://developers.google.com/events/gdd-europe/">Google Developers Days Europe</a> a couple of weeks ago, I was introduced to Firebase’s new Cloud Functions feature. Firebase Cloud Functions allow us to write server-side code without the need to create and manage our own backend, and the functions we create can be as simple or complex as we need them to be.</p> <p>One particular use case I was interested in was how to use Firebase to set up Web Push Notifications feature from client-side to server-side. There is a really great <a href="https://codelabs.developers.google.com/codelabs/firebase-cloud-functions/">Codelab</a> that demonstrates this, but I decided to create a small project myself to highlight just this feature.</p> <h2 id="simply-notify" tabindex="-1">Simply Notify <a class="header-anchor" href="https://bitsofco.de/web-push-notifications-with-firebase/">#</a></h2> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/00G2NAc-1m-1758.avif 1758w"><source type="image/webp" srcset="https://bitsofco.de/img/00G2NAc-1m-1758.webp 1758w"><img alt="Simply Notify Screenshot" loading="lazy" decoding="async" src="https://bitsofco.de/img/00G2NAc-1m-1758.png" width="1758" height="1088"></picture></p> <p><a href="https://simply-notify.firebaseapp.com">Simply Notify</a> is a simple application that just does one thing - it sends a push notification with a message to everyone subscribed.</p> <p>To demonstrate how I built it, I created a short video series. You can watch the videos below:</p> <h3 id="part-1-introduction-and-firebase-setup" tabindex="-1">Part 1 - Introduction &amp; Firebase Setup <a class="header-anchor" href="https://bitsofco.de/web-push-notifications-with-firebase/">#</a></h3> <p><a href="https://www.youtube.com/watch?v=XdzXaW8IbBM"><picture><source type="image/avif" srcset="https://bitsofco.de/img/cAiW8UxJqr-1280.avif 1280w"><source type="image/webp" srcset="https://bitsofco.de/img/cAiW8UxJqr-1280.webp 1280w"><img alt="Part 1 - Introduction &amp; Firebase Setup" loading="lazy" decoding="async" src="https://bitsofco.de/img/cAiW8UxJqr-1280.png" width="1280" height="720"></picture></a></p> <h3 id="part-2-authentication" tabindex="-1">Part 2 - Authentication <a class="header-anchor" href="https://bitsofco.de/web-push-notifications-with-firebase/">#</a></h3> <p><a href="https://www.youtube.com/watch?v=cnwuYeCqni0"><picture><source type="image/avif" srcset="https://bitsofco.de/img/j0bgPzXvUQ-1280.avif 1280w"><source type="image/webp" srcset="https://bitsofco.de/img/j0bgPzXvUQ-1280.webp 1280w"><img alt="Part 2 - Authentication" loading="lazy" decoding="async" src="https://bitsofco.de/img/j0bgPzXvUQ-1280.png" width="1280" height="720"></picture></a></p> <h3 id="part-3-subscribing-to-notifications" tabindex="-1">Part 3 - Subscribing to Notifications <a class="header-anchor" href="https://bitsofco.de/web-push-notifications-with-firebase/">#</a></h3> <p><a href="https://www.youtube.com/watch?v=a50fz6oiLCQ"><picture><source type="image/avif" srcset="https://bitsofco.de/img/uyplpOyLU8-1280.avif 1280w"><source type="image/webp" srcset="https://bitsofco.de/img/uyplpOyLU8-1280.webp 1280w"><img alt="Part 3 - Subscribing to Notifications" loading="lazy" decoding="async" src="https://bitsofco.de/img/uyplpOyLU8-1280.png" width="1280" height="720"></picture></a></p> <h3 id="part-4-creating-and-saving-messages" tabindex="-1">Part 4 - Creating &amp; Saving Messages <a class="header-anchor" href="https://bitsofco.de/web-push-notifications-with-firebase/">#</a></h3> <p><a href="https://www.youtube.com/watch?v=YCMtarwgNIo"><picture><source type="image/avif" srcset="https://bitsofco.de/img/vUGcePO1vd-1280.avif 1280w"><source type="image/webp" srcset="https://bitsofco.de/img/vUGcePO1vd-1280.webp 1280w"><img alt="Part 4 - Creating &amp; Saving Messages" loading="lazy" decoding="async" src="https://bitsofco.de/img/vUGcePO1vd-1280.png" width="1280" height="720"></picture></a></p> <h3 id="part-5-sending-web-notifications" tabindex="-1">Part 5 - Sending Web Notifications <a class="header-anchor" href="https://bitsofco.de/web-push-notifications-with-firebase/">#</a></h3> <p><a href="https://www.youtube.com/watch?v=rumJsnHbXsI"><picture><source type="image/avif" srcset="https://bitsofco.de/img/JLItMiHklo-1280.avif 1280w"><source type="image/webp" srcset="https://bitsofco.de/img/JLItMiHklo-1280.webp 1280w"><img alt="Part 5 - Sending Web Notifications" loading="lazy" decoding="async" src="https://bitsofco.de/img/JLItMiHklo-1280.png" width="1280" height="720"></picture></a></p> <h3 id="part-6-database-security-and-optimisations" tabindex="-1">Part 6 - Database Security &amp; Optimisations <a class="header-anchor" href="https://bitsofco.de/web-push-notifications-with-firebase/">#</a></h3> <p><a href="https://www.youtube.com/watch?v=4BJ-Ey9aVrM"><picture><source type="image/avif" srcset="https://bitsofco.de/img/G0nkWbuAxX-1280.avif 1280w"><source type="image/webp" srcset="https://bitsofco.de/img/G0nkWbuAxX-1280.webp 1280w"><img alt="Part 6 - Database Security &amp; Optimisations" loading="lazy" decoding="async" src="https://bitsofco.de/img/G0nkWbuAxX-1280.png" width="1280" height="720"></picture></a></p> Accessibility Testing with pa11y 2017-08-15T00:00:00Z https://bitsofco.de/pa11y/ <p>Knowing where to start with accessibility testing can be difficult and overwhelming. In my article on <a href="https://bitsofco.de/tools-for-developing-accessible-websites">Tools for Developing Accessible Websites</a>, I mentioned 5 tools you can use to get started developing more accessible websites. More recently, I have discovered an incredibly powerful tool - <a href="https://pa11y.org">pa11y</a></p> <h2 id="introducing-pa11y" tabindex="-1">Introducing pa11y <a class="header-anchor" href="https://bitsofco.de/pa11y/">#</a></h2> <p><a href="https://pa11y.org/">pa11y</a>, pronounced pally, is a set of free and open source tools that aims to make designing and developing accessibility easier. They have a number of different projects to help with this, for example a web dashboard interface, but I will be focusing on 2 of their projects: <a href="https://github.com/pa11y/pa11y">pa11y</a> and <a href="https://github.com/pa11y/ci">pa11y-ci</a>.</p> <h2 id="getting-started-with-pa11y" tabindex="-1">Getting Started with pa11y <a class="header-anchor" href="https://bitsofco.de/pa11y/">#</a></h2> <p>As a beginner, the easiest way to get started with accessibility testing is by installing the core package, <code>pa11y</code>.</p> <pre class="language-bash" tabindex="0"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> <span class="token parameter variable">-g</span> pa11y</code></pre> <p>This gives us access to the command <code>pa11y</code>, which we can use to run a simple accessibility audit on a particular url. Let's take this blog's homepage as an example.</p> <pre class="language-bash" tabindex="0"><code class="language-bash">pa11y https://bitsofco.de</code></pre> <p>Running the above command, however, will likely send you into spiral of doom because <em>every single</em> error, warning, or notice will be outputted into your terminal. But don't fret, because there are a few simple options you can pass to ease you into the process.</p> <h3 id="only-display-critical-errors" tabindex="-1">Only display critical errors <a class="header-anchor" href="https://bitsofco.de/pa11y/">#</a></h3> <p>When first starting out, you will likely <em>just</em> want to see the crucial errors that you need to fix, and not just every single accessibility-related message related to the page. pa11y divides their messages into three types:</p> <ol> <li> <p>A <code>notice</code> is general message about an element's accessibility. It isn't necessarily something that is wrong with your implementation, but rather a notice to be aware of concerning a particular element. For example, a notice can be &quot;Notice: Check that the title element describes the document.&quot;</p> </li> <li> <p>A <code>warning</code> is something that pa11y considers may be an issue, so you should take a look at it. For example, a warning you may receive on an <code>&lt;img&gt;</code> element with a null <code>alt</code> attribute is &quot;Warning: Img element is marked so that it is ignored by Assistive Technology.&quot;. Using a null <code>alt</code> attribute isn't <em>necessarily</em> an error, as null attributes can be intentional (see <a href="https://bitsofco.de/alternative-text-and-images">Alternative Text and Images</a>), but it is something that you should look at to make sure</p> </li> <li> <p>An <code>error</code> is a critical issue that should be fixed. For example, an error you may receive on an unlabelled for input is &quot;Error: This text input element does not have a name available to an accessibility API.&quot;</p> </li> </ol> <p>For your first audit, you probably want to exclude notices and warnings, so you only see the crucial errors on your page. This can be done by using the <code>--ignore</code> flag -</p> <pre class="language-bash" tabindex="0"><code class="language-bash">pa11y https://bitsofco.de <span class="token parameter variable">--ignore</span> <span class="token string">"warning;notice"</span></code></pre> <h3 id="set-thresholds-for-failure" tabindex="-1">Set thresholds for failure <a class="header-anchor" href="https://bitsofco.de/pa11y/">#</a></h3> <p>Once you've set your option to only see critical error messages, you can specify the amount of those errors that should be permitted in order for the page to &quot;pass&quot; the accessibility test. This is done using the <code>--threshold</code> flag -</p> <pre class="language-bash" tabindex="0"><code class="language-bash">pa11y https://bitsofco.de <span class="token parameter variable">--threshold</span> <span class="token number">10</span></code></pre> <p>The command above, for example, will allow up to 10 errors on the page before the page will be deemed to have failed the test.</p> <h3 id="hide-elements" tabindex="-1">Hide elements <a class="header-anchor" href="https://bitsofco.de/pa11y/">#</a></h3> <p>Finally, another great option for beginners is to hide certain elements from testing. This can be useful if, for example, you have third party widgets such as ads on your page that you can't control. Or, you can use it to hide elements that are visual or screen-reader only.</p> <pre class="language-bash" tabindex="0"><code class="language-bash">pa11y <span class="token operator">&lt;</span>url<span class="token operator">></span> --hide-elements <span class="token string">"#ads .sr-only [aria-role='presentation']"</span></code></pre> <p>Using this blog as an example, I want to hide the errors related to Carbon Ads widget (<code>#cardbonads</code>) -</p> <pre class="language-bash" tabindex="0"><code class="language-bash">pa11y https://bitsofco.de --hide-elements <span class="token string">"#carbonads"</span></code></pre> <h3 id="putting-it-together" tabindex="-1">Putting it together <a class="header-anchor" href="https://bitsofco.de/pa11y/">#</a></h3> <p>Putting all of these together, you should get a useful, non overwhelming audit of your page, with actionable errors to fix.</p> <pre class="language-bash" tabindex="0"><code class="language-bash">pa11y https://bitsofco.de <span class="token parameter variable">--ignore</span> <span class="token string">"warning;notice"</span> <span class="token parameter variable">--threshold</span> <span class="token number">10</span> --hide-elements <span class="token string">"#carbonads"</span></code></pre> <h2 id="doing-more-with-pa11y" tabindex="-1">Doing more with pa11y <a class="header-anchor" href="https://bitsofco.de/pa11y/">#</a></h2> <p>Using pa11y in the way described above can be great when you're getting started, but pa11y can be used for much, <em>much</em> more than that.</p> <p>The basic pa11y command starts to fall a bit short when you need to test multiple pages, each with different options. Although the core package can be configured in a more advanced way by creating a dedicated node script, I think the <code>pa11y-ci</code> package does a better job at handling the more complex audits.</p> <pre class="language-bash" tabindex="0"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> <span class="token parameter variable">-g</span> pa11y-ci</code></pre> <p>This will give you access to the <code>pa11y-ci</code> command. It can be used in a similar way to <code>pa11y</code>, but most of the configuration options are stored in a <code>.pa11yci</code> JSON file.</p> <p>Here's an example of the same basic configuration mentioned above, but in a <code>.pa11yci</code> file:</p> <pre class="language-json" tabindex="0"><code class="language-json"><span class="token punctuation">{</span><br> <span class="token property">"defaults"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token property">"hideElements"</span><span class="token operator">:</span> <span class="token string">"#carbonads"</span><span class="token punctuation">,</span><br> <span class="token property">"ignore"</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">"notice"</span><span class="token punctuation">,</span> <span class="token string">"warning"</span> <span class="token punctuation">]</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token property">"urls"</span><span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token string">"https://bitsofco.de"</span> <span class="token punctuation">]</span><br><span class="token punctuation">}</span></code></pre> <p>The only option that is still specified as a command line option, is threshold. So, we can execute all our options using the following command:</p> <pre class="language-bash" tabindex="0"><code class="language-bash">pa11y-ci <span class="token parameter variable">--threshold</span> <span class="token number">10</span></code></pre> <p><small>(Note that <code>pa11y-ci</code> expects your <code>.pa11yci</code> file to be in the root directory.)</small></p> <p>Once we've got the default configurations down, we can move on to some more advanced customisations.</p> <h3 id="testing-multiple-urls" tabindex="-1">Testing multiple URLS <a class="header-anchor" href="https://bitsofco.de/pa11y/">#</a></h3> <p>The first thing you might notice about the <code>.pa11yci</code> configuration, is that there is an array of URLs we can pass. This allows us to test multiple URLs in one go. Using this blog example, I may want to test the home page, the newsletter signup page, and a single article page.</p> <pre class="language-json" tabindex="0"><code class="language-json"><span class="token punctuation">{</span><br> <span class="token property">"urls"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token string">"https://bitsofco.de"</span> <span class="token punctuation">,</span><br> <span class="token string">"https://bitsofco.de/subscribe"</span><span class="token punctuation">,</span><br> <span class="token string">"https://bitsofco.de/collapsible-margins"</span><br> <span class="token punctuation">]</span><br><span class="token punctuation">}</span></code></pre> <p>What's even greater, is that we can specify custom configurations for each page. We do this by passing an object into the <code>urls</code>, instead of a simple string. In addition to the <code>url</code>, we pass any of the <a href="https://github.com/pa11y/pa11y#configuration">pa11y configurations</a> we want to override for that particular page.</p> <p>For example, I want the <code>#carbonads</code> element to be hidden globally because it shows up on all pages. On an article page, I also want to hide the comments (<code>#disqus_thread</code>), so I can set a custom <code>hideElements</code> configuration only for that page.</p> <pre class="language-json" tabindex="0"><code class="language-json"><span class="token punctuation">{</span><br> <span class="token property">"defaults"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token property">"hideElements"</span><span class="token operator">:</span> <span class="token string">"#carbonads"</span><br> <span class="token punctuation">}</span><br> <span class="token property">"urls"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token string">"https://bitsofco.de"</span><span class="token punctuation">,</span><br> <span class="token string">"https://bitsofco.de/subscribe"</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token property">"url"</span><span class="token operator">:</span> <span class="token string">"https://bitsofco.de/collapsible-margins"</span><span class="token punctuation">,</span><br> <span class="token property">"hideElements"</span><span class="token operator">:</span> <span class="token string">"#carbonads, #disqus_thread"</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> <h3 id="customise-accessibility-standards-and-rules" tabindex="-1">Customise accessibility standards and rules <a class="header-anchor" href="https://bitsofco.de/pa11y/">#</a></h3> <p>A useful configuration option is the <code>standard</code> option. The default accessibility standard used by pa11y to test a page is the <a href="https://www.w3.org/TR/WCAG20/#conformance-reqs">WCAG2AA</a>. However, we can change it to any of the following options - Section508, WCAG2A, WCAG2AA, or WCAG2AAA.</p> <pre class="language-json" tabindex="0"><code class="language-json"><span class="token punctuation">{</span><br> <span class="token property">"defaults"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token property">"standard"</span><span class="token operator">:</span> <span class="token string">"WCAG2AAA"</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p>Additionally, we can specify a set of specific WCAG 2.0 guidelines to include. For example, you may want to follow the WCAG2A standard in general, but include a couple of specific guidelines that would normally apply to a stricter standard.</p> <pre class="language-json" tabindex="0"><code class="language-json"><span class="token punctuation">{</span><br> <span class="token property">"defaults"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token property">"standard"</span><span class="token operator">:</span> <span class="token string">"WCAG2A"</span><span class="token punctuation">,</span><br> <span class="token property">"rules"</span><span class="token operator">:</span> <span class="token punctuation">[</span> 'Principle1.Guideline1_4.1_4_6_AAA' <span class="token punctuation">]</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p>The above requires that the specific guideline 1.4.6, &quot;Contrast (Enhanced)&quot; must be followed.</p> <h3 id="testing-user-actions" tabindex="-1">Testing user actions <a class="header-anchor" href="https://bitsofco.de/pa11y/">#</a></h3> <p>Besides a basic audit of your markup, pa11y can be used to test actions that a user might take on your page. Using their <a href="https://github.com/pa11y/pa11y#actions">custom set of keywords</a>, you can demo a flow of actions.</p> <p>For example, on the subscribe page of this blog, I may want to test that a user can actually fill out the form to subscribe.</p> <pre class="language-json" tabindex="0"><code class="language-json"><span class="token property">"urls"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token property">"url"</span><span class="token operator">:</span> <span class="token string">"https://bitsofco.de/subscribe"</span><span class="token punctuation">,</span><br> <span class="token property">"actions"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token string">"set field #mce-EMAIL to pa11y@testing.com"</span><span class="token punctuation">,</span><br> <span class="token string">"set field #mce-FNAME to Tester"</span><span class="token punctuation">,</span><br> <span class="token string">"click element #mc-embedded-subscribe"</span><span class="token punctuation">,</span><br> <span class="token string">"wait for url to not be https://bitsofco.de/subscribe"</span><br> <span class="token punctuation">]</span><span class="token punctuation">,</span><br> <span class="token property">"timeout"</span><span class="token operator">:</span> <span class="token number">60000</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">]</span></code></pre> <p>When testing actions, it's important to set a long <code>timeout</code> to allow pa11y to go through all the steps.</p> <h3 id="device-specific-testing" tabindex="-1">Device-specific testing <a class="header-anchor" href="https://bitsofco.de/pa11y/">#</a></h3> <p>pa11y allows us to specify the viewport dimensions to test a page with, allowing for device-specific testing.</p> <pre class="language-json" tabindex="0"><code class="language-json"><span class="token punctuation">{</span><br> <span class="token property">"defaults"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token property">"page"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token property">"viewport"</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"width"</span><span class="token operator">:</span> <span class="token number">320</span><span class="token punctuation">,</span> <span class="token property">"height"</span><span class="token operator">:</span> <span class="token number">480</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="doing-even-more-with-pa11y" tabindex="-1">Doing even more with pa11y <a class="header-anchor" href="https://bitsofco.de/pa11y/">#</a></h2> <p>pa11y has what seems like an endless list of features and configuration options so we can test our sites in any number of ways. I haven't gone over every feature or option available, such as implementing continuous integration which the the <code>pa11y-ci</code> package is actually built for. If you want to find out more, check out the repositories for <a href="https://github.com/pa11y/pa11y">pa11y</a> and <a href="https://github.com/pa11y/ci">pa11y-ci</a>. If there are any particular features you would like me to cover in more detail, leave a comment below.</p> <p>You can see a full example of how I use pa11y to test this blog on my repository, <a href="https://github.com/ireade/bitsofcode-pa11y">ireade/bitsofcode-pa11y</a>.</p> What's the Deal with Collapsible Margins? 2017-07-04T00:00:00Z https://bitsofco.de/collapsible-margins/ <p>Last week, I ran a twitter poll asking the following question - <strong>What would be the amount of space between two sibling divs, where the 1st has a margin-bottom of 10px and 2nd a margin-top of 30px?</strong></p> <p>The result of this poll was that 39% of the 754 people who voted got the correct answer, which was 30px.</p> <blockquote> <p>[CSS Quiz] What would be the amount of space between two sibling divs, where the 1st has a margin-bottom of 10px and 2nd margin-top 30px? — Ire Aderinokun (@ireaderinokun) <a href="https://twitter.com/ireaderinokun/status/879636642717413380">June 27, 2017</a></p> </blockquote> <p>The reason the resulting margin would be 30px was because of a feature of CSS called “Collapsing margins”.</p> <h2 id="a-refresh-on-the-css-box-model" tabindex="-1">A Refresh on the CSS Box Model <a class="header-anchor" href="https://bitsofco.de/collapsible-margins/">#</a></h2> <p>Before we get into how collapsing margins work, we need to revisit the box model. As I mentioned in my more in-depth article on <a href="https://bitsofco.de/controlling-the-box-model">Controlling the Box Model</a>, every element in the document tree is a rectangular box made up of four areas - the content area, padding area, border area, and margin area.</p> <p>The margin area is the space outside an element’s border. Let’s take this <code>.box</code> element, for example -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.box</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 300px<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 300px<span class="token punctuation">;</span><br> <span class="token property">padding</span><span class="token punctuation">:</span> 50px<span class="token punctuation">;</span><br> <span class="token property">border</span><span class="token punctuation">:</span> 50px solid grey<span class="token punctuation">;</span><br> <span class="token property">margin</span><span class="token punctuation">:</span> 50px<span class="token punctuation">;</span> <span class="token comment">/* margin area */</span><br><span class="token punctuation">}</span></code></pre> <p>The margin area of this box is shown striped below:</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/o_E9Ks4qi_-1065.avif 1065w"><source type="image/webp" srcset="https://bitsofco.de/img/o_E9Ks4qi_-1065.webp 1065w"><img alt="50px Margin Area on box element" loading="lazy" decoding="async" src="https://bitsofco.de/img/o_E9Ks4qi_-1065.jpeg" width="1065" height="698"></picture></p> <p>The margin area is different to the other three areas of the box model because it is not technically part of the element itself. Even if we specify a set margin for an element in our CSS, the actual margin painted on the document can be affected by the other elements within the document, as we will see in the case of collapsing margins.</p> <h2 id="what-is-a-collapsed-margin" tabindex="-1">What is a Collapsed Margin? <a class="header-anchor" href="https://bitsofco.de/collapsible-margins/">#</a></h2> <p>A collapsed margin is what occurs when two block-level elements with meeting vertical margins combine. When this happens, the larger of the two margins (or any if they are equal) is assumed as the single collapsed margin.</p> <p>Let’s take the example from my Twitter poll. We have two elements; Element A has a bottom margin of 10px, and element B has a top margin of 30px. Here are the two elements shown separately -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/qeVZZluJTj-640.avif 640w"><source type="image/webp" srcset="https://bitsofco.de/img/qeVZZluJTj-640.webp 640w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/qeVZZluJTj-640.png" width="640" height="273"></picture></p> <p>If we place these two, block-level, elements one after the other in our markup, this is the result we get -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/uIyLXQn2zE-640.avif 640w"><source type="image/webp" srcset="https://bitsofco.de/img/uIyLXQn2zE-640.webp 640w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/uIyLXQn2zE-640.png" width="640" height="287"></picture></p> <p>The margin between the two elements is combined and the resulting “collapsed” margin with a height of 30px is what is left.</p> <h2 id="when-do-margins-collapse" tabindex="-1">When do Margins Collapse? <a class="header-anchor" href="https://bitsofco.de/collapsible-margins/">#</a></h2> <p>As a general rule, <strong>adjoining vertical margins between in-flow, block-level, boxes will always collapse</strong>. There are four scenarios in which this takes place:</p> <h3 id="case-1-the-top-margins-of-a-parent-and-it-s-first-child" tabindex="-1">Case 1: The top margins of a parent and it’s first child <a class="header-anchor" href="https://bitsofco.de/collapsible-margins/">#</a></h3> <p>If a top margin is applied to a parent element as well as it’s first in-flow child, those margins may be collapsed together.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.parent</span> <span class="token punctuation">{</span><br> <span class="token property">margin-top</span><span class="token punctuation">:</span> 30px<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 150px<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>200<span class="token punctuation">,</span>200<span class="token punctuation">,</span>200<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Grey */</span><br><span class="token punctuation">}</span><br><br><span class="token selector">.child</span> <span class="token punctuation">{</span><br> <span class="token property">margin-top</span><span class="token punctuation">:</span> 30px<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">height</span><span class="token punctuation">:</span> 100px<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>250<span class="token punctuation">,</span> 219<span class="token punctuation">,</span> 92<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Yellow */</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/QuopgbYB6D-640.avif 640w"><source type="image/webp" srcset="https://bitsofco.de/img/QuopgbYB6D-640.webp 640w"><img alt="30px collapsed margin between parent and first child" loading="lazy" decoding="async" src="https://bitsofco.de/img/QuopgbYB6D-640.png" width="640" height="209"></picture></p> <h3 id="case-2-the-bottom-margins-of-a-parent-and-it-s-last-child" tabindex="-1">Case 2: The bottom margins of a parent and it’s last child <a class="header-anchor" href="https://bitsofco.de/collapsible-margins/">#</a></h3> <p>Similarly to the first example, margins may be collapsed if both a parent and last in-flow child have a bottom margin. However, unlike the top margins, bottom margins will only collapse if the parent has a computed height of <code>auto</code>.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.parent</span> <span class="token punctuation">{</span><br> <span class="token property">margin-bottom</span><span class="token punctuation">:</span> 30px<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>200<span class="token punctuation">,</span>200<span class="token punctuation">,</span>200<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">.child</span> <span class="token punctuation">{</span><br> <span class="token property">margin-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> 100px<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 100px<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>250<span class="token punctuation">,</span> 219<span class="token punctuation">,</span> 92<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Xql8euNlN2-640.avif 640w"><source type="image/webp" srcset="https://bitsofco.de/img/Xql8euNlN2-640.webp 640w"><img alt="30px collapsed margin between parent and last child" loading="lazy" decoding="async" src="https://bitsofco.de/img/Xql8euNlN2-640.png" width="640" height="190"></picture></p> <h3 id="case-3-the-bottom-and-top-margins-of-siblings" tabindex="-1">Case 3: The bottom and top margins of siblings <a class="header-anchor" href="https://bitsofco.de/collapsible-margins/">#</a></h3> <p>This is the scenario I mentioned in my poll. If we have two elements, where one has a bottom margin and the second in-flow sibling has a top margin, those margins may be collapsed.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.sibling-one</span> <span class="token punctuation">{</span><br> <span class="token property">margin-bottom</span><span class="token punctuation">:</span> 10px<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">height</span><span class="token punctuation">:</span> 100px<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>250<span class="token punctuation">,</span> 219<span class="token punctuation">,</span> 92<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">.sibling-two</span> <span class="token punctuation">{</span><br> <span class="token property">margin-top</span><span class="token punctuation">:</span> 30px<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">height</span><span class="token punctuation">:</span> 100px<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>200<span class="token punctuation">,</span>200<span class="token punctuation">,</span>200<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/uIyLXQn2zE-640.avif 640w"><source type="image/webp" srcset="https://bitsofco.de/img/uIyLXQn2zE-640.webp 640w"><img alt="30px collapsed margin between siblings" loading="lazy" decoding="async" src="https://bitsofco.de/img/uIyLXQn2zE-640.png" width="640" height="287"></picture></p> <h3 id="case-4-empty-elements" tabindex="-1">Case 4: Empty elements <a class="header-anchor" href="https://bitsofco.de/collapsible-margins/">#</a></h3> <p>Finally, both the top and bottom margins on a single element may collapse if the element has a computed height of <code>0</code>, i.e. if it is an empty element.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>This paragraph is before the empty element<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>empty<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token comment">&lt;!-- Empty element --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>This paragraph is after the empty element<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.empty</span> <span class="token punctuation">{</span><br> <span class="token property">margin-bottom</span><span class="token punctuation">:</span> 30px<span class="token punctuation">;</span><br> <span class="token property">margin-top</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/KcAapOAPtD-640.avif 640w"><source type="image/webp" srcset="https://bitsofco.de/img/KcAapOAPtD-640.webp 640w"><img alt="30px collapsed margin within single empty element" loading="lazy" decoding="async" src="https://bitsofco.de/img/KcAapOAPtD-640.png" width="640" height="151"></picture></p> <h3 id="exceptions" tabindex="-1">Exceptions <a class="header-anchor" href="https://bitsofco.de/collapsible-margins/">#</a></h3> <p>As with everything, there are a few exceptions to take note of for when margins will <strong>not</strong> collapse.</p> <h4 id="flexbox-grid-and-other-non-block-level-elements" tabindex="-1">Flexbox, Grid, and other non-block-level elements <a class="header-anchor" href="https://bitsofco.de/collapsible-margins/">#</a></h4> <p>Collapsible margins only apply to block-level elements. These are elements that have either of the following values for the <code>display</code> property: <code>block</code>, <code>list-item</code>, or <code>table</code>. Therefore, flex items, grid items, absolutely positioned items, and other non-block-level elements do not apply.</p> <h4 id="the-root-element" tabindex="-1">The root element <a class="header-anchor" href="https://bitsofco.de/collapsible-margins/">#</a></h4> <p>The margins of the root element’s box will never collapse.</p> <h4 id="lines-boxes-clearance-paddings-and-borders" tabindex="-1">Lines boxes, clearance, paddings, and borders <a class="header-anchor" href="https://bitsofco.de/collapsible-margins/">#</a></h4> <p>In the example cases mentioned above, collapsing will not happen if there are line boxes, clearance, paddings, or borders, that stand between the two elements.</p> <p>Let’s take the first example I mentioned, where the top margin of the parent is collapsed with the top margin of it's first child. If we add a border around the parent element, the margins no longer collapse.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/y-J7WZXadu-640.avif 640w"><source type="image/webp" srcset="https://bitsofco.de/img/y-J7WZXadu-640.webp 640w"><img alt="No collapsed margins when parent element has a border" loading="lazy" decoding="async" src="https://bitsofco.de/img/y-J7WZXadu-640.png" width="640" height="200"></picture></p> <p>This happens because the margin of the parent is no longer in direct contact with the margin of the child.</p> <h2 id="dealing-with-collapsed-margins" tabindex="-1">Dealing with Collapsed Margins <a class="header-anchor" href="https://bitsofco.de/collapsible-margins/">#</a></h2> <p>Collapsible margins can be a pain if you don't properly understand when they occur. The first step to dealing with or avoiding them is understand exactly which case of collapsible margins we are dealing with.</p> <p>Collapsed margins that occur because of empty elements or parent/child relationships can't really be avoided. The only way to counteract collapsible margins that occur this way is by inserting something between the elements, for example a border. Otherwise, changing the element's display status to something that is not block-level would be an option.</p> <p>Collapsed margins that occur because of adjoining sibling elements, on the other hand, can be avoided with a change in CSS writing style. Personally, I prefer to follow Harry Roberts' rule of <a href="https://csswizardry.com/2012/06/single-direction-margin-declarations/">Single-direction margin declarations</a>, which has other benefits besides helping to avoid collapsible margins.</p> How the minmax() Function Works 2017-06-06T00:00:00Z https://bitsofco.de/how-the-minmax-function-works/ <p>One incredibly useful new feature introduced with the <a href="https://www.w3.org/TR/css-grid-1">CSS Grid Layout Specification</a> is the <code>minmax()</code> function. This function opens the door to us being able to write much more powerful and succinct CSS by allowing us to set, as a value for a <a href="https://bitsofco.de/how-the-minmax-function-works/">grid track</a>, a function including both a minimum and maximum value.</p> <h2 id="the-minmax-function" tabindex="-1">The <code>minmax()</code> Function <a class="header-anchor" href="https://bitsofco.de/how-the-minmax-function-works/">#</a></h2> <p>The <code>minmax()</code> function accepts two parameters, a minimum value and a maximum value.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token function">minmax</span><span class="token punctuation">(</span>min<span class="token punctuation">,</span> max<span class="token punctuation">)</span></code></pre> <p>If the maximum value defined is actually less than the minimum, it is ignored, and the function is treated as purely presenting a minimum value.</p> <p>The <code>minmax()</code> function accepts 6 types of value:</p> <ul> <li>Length</li> <li>Percentage</li> <li>Flexible Length</li> <li><code>max-content</code></li> <li><code>min-content</code></li> <li><code>auto</code></li> </ul> <p>Let’s consider examples of each.</p> <h3 id="length" tabindex="-1">Length <a class="header-anchor" href="https://bitsofco.de/how-the-minmax-function-works/">#</a></h3> <p>Perhaps the most simple value we can use with the <code>minmax()</code> function is a basic length. Take, for example, this simple grid, which has three columns and a single row.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/WjwSu0YsvK-1462.avif 1462w"><source type="image/webp" srcset="https://bitsofco.de/img/WjwSu0YsvK-1462.webp 1462w"><img alt="3 Column by 1 Row Grid" loading="lazy" decoding="async" src="https://bitsofco.de/img/WjwSu0YsvK-1462.png" width="1462" height="378"></picture></p> <p>Using the <code>minmax()</code> function, we can specify that the yellow grid cell remain between 100px and 200px. As the viewport is resized, the absolute value will change, but always within these two limits.</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-columns</span><span class="token punctuation">:</span> <span class="token function">minmax</span><span class="token punctuation">(</span>100px<span class="token punctuation">,</span> 200px<span class="token punctuation">)</span> 1fr 1fr<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/omHjpJWuEF-884.avif 884w"><source type="image/gif" srcset="https://bitsofco.de/img/omHjpJWuEF-884.gif 884w"><img alt="Minmax Function: 100px and 200px" loading="lazy" decoding="async" src="https://bitsofco.de/img/omHjpJWuEF-884.webp" width="884" height="166"></picture></p> <p>The second and third columns shrink and expand to fill the remaining available space equally, but the first column always remains between 100px and 200px wide.</p> <h3 id="percentage" tabindex="-1">Percentage <a class="header-anchor" href="https://bitsofco.de/how-the-minmax-function-works/">#</a></h3> <p>Besides the basic length units, we can also use percentage units with the <code>minmax()</code> function. Say, for example, that we want the yellow grid cell to take up a maximum of 50% of the grid, but not get any smaller than 200px.</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-columns</span><span class="token punctuation">:</span> <span class="token function">minmax</span><span class="token punctuation">(</span>200px<span class="token punctuation">,</span> 50%<span class="token punctuation">)</span> 1fr 1fr<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/lDiBpEKsoZ-838.avif 838w"><source type="image/gif" srcset="https://bitsofco.de/img/lDiBpEKsoZ-838.gif 838w"><img alt="Minmax Function: 200px and 50%" loading="lazy" decoding="async" src="https://bitsofco.de/img/lDiBpEKsoZ-838.webp" width="838" height="230"></picture></p> <p>No matter how small we shrink the viewport, the yellow cell never gets smaller than 200px. But, when it has the room to, is grows to a maximum of half the grid width.</p> <h3 id="flexible-length" tabindex="-1">Flexible Length <a class="header-anchor" href="https://bitsofco.de/how-the-minmax-function-works/">#</a></h3> <p>The Flexible Length is a new unit which, like the <code>minmax()</code> function, was also introduced with the CSS Grid Layout Specification. This length, which uses the <code>fr</code> unit, represents a fraction of the free space in the grid container. For example, let’s say we have a 100px-wide grid, with 2 columns. One column has a fixed width of 20px and the other column has a width of 1fr. The second column will effectively have a width of 80px, as it takes up the remaining available space within the grid.</p> <p>Currently, the <code>fr</code> unit can only be used as the maximum value in the <code>minmax()</code> function (although it is <a href="https://www.w3.org/TR/css-grid-1/#valdef-grid-template-columns-minmax">noted in the spec</a> that, in the future, it may be applicable to the minimum value as well). Going back to our example, we can specify that we want the yellow grid cell to be a minimum of 200px. If the viewport is resized larger than that, we want the cell to be 1fr, the same size as the other two columns.</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-columns</span><span class="token punctuation">:</span> <span class="token function">minmax</span><span class="token punctuation">(</span>200px<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span> 1fr 1fr<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/GovzMW2eTc-884.avif 884w"><source type="image/gif" srcset="https://bitsofco.de/img/GovzMW2eTc-884.gif 884w"><img alt="Minmax Function: 200px and 1fr" loading="lazy" decoding="async" src="https://bitsofco.de/img/GovzMW2eTc-884.webp" width="884" height="166"></picture></p> <p>Since all columns are 1fr at larger viewport sizes, they share an equal amount of space within the grid.</p> <h3 id="max-content" tabindex="-1"><code>max-content</code> <a class="header-anchor" href="https://bitsofco.de/how-the-minmax-function-works/">#</a></h3> <p>The <code>max-content</code> keyword is a special value that represents the cell’s &quot;ideal size&quot;. This is the smallest possible size the cell can be, while still fitting around it’s contents unobtrusively. For example, if the contents of the cell is a sentence, the ideal width for the cell will be to take up the entire length of the sentence, regardless of the length and no line breaks.</p> <p>Taking our previous example, let’s specify that the yellow grid cell be both a minimum and maximum of <code>max-content</code>.</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-columns</span><span class="token punctuation">:</span> <span class="token function">minmax</span><span class="token punctuation">(</span>max-content<span class="token punctuation">,</span> max-content<span class="token punctuation">)</span> 1fr 1fr<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Dbv9p0mqjh-884.avif 884w"><source type="image/gif" srcset="https://bitsofco.de/img/Dbv9p0mqjh-884.gif 884w"><img alt="Minmax Function: max-content" loading="lazy" decoding="async" src="https://bitsofco.de/img/Dbv9p0mqjh-884.webp" width="884" height="166"></picture></p> <p>As we can see, the size of the column expands to fit the entire length of the string. Since both the minimum and maximum values are set to <code>max-content</code>, the width of the column remains the same.</p> <h3 id="min-content" tabindex="-1"><code>min-content</code> <a class="header-anchor" href="https://bitsofco.de/how-the-minmax-function-works/">#</a></h3> <p>The <code>min-content</code> keyword, like <code>max-content</code>, is a special value. It represents the smallest possible size the cell can be that does not lead to an overflow, unless that overflow is unavoidable.</p> <p>To illustrate the difference between <code>min-content</code> and <code>max-content</code>, we can use the same content as in the previous example, but set both values of the <code>minmax()</code> function to <code>min-content</code>.</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-columns</span><span class="token punctuation">:</span> <span class="token function">minmax</span><span class="token punctuation">(</span>min-content<span class="token punctuation">,</span> min-content<span class="token punctuation">)</span> 1fr 1fr<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Qte_J54SuI-894.avif 894w"><source type="image/gif" srcset="https://bitsofco.de/img/Qte_J54SuI-894.gif 894w"><img alt="Minmax Function: min-content" loading="lazy" decoding="async" src="https://bitsofco.de/img/Qte_J54SuI-894.webp" width="894" height="308"></picture></p> <p>We can see that the content within the cell is shifted in order to take up the least amount of horizontal space possible, without causing any overflow.</p> <h3 id="auto" tabindex="-1"><code>auto</code> <a class="header-anchor" href="https://bitsofco.de/how-the-minmax-function-works/">#</a></h3> <p>Finally, we have <code>auto</code>. This has different meanings depending on if it is used as the maximum or minimum value in the <code>minmax()</code> function.</p> <p>If used as a maximum, the <code>auto</code> value is equivalent to the <code>max-content</code> value. If used as a minimum, the <code>auto</code> value represents the largest minimum size the cell can be. This &quot;largest minimum size&quot; is different from the <code>min-content</code> value, and specified by <code>min-width</code>/<code>min-height</code>.</p> <p>To illustrate this, here's what happens when the yellow grid cell in our example is set to an <code>auto</code> minimum and maximum.</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-columns</span><span class="token punctuation">:</span> <span class="token function">minmax</span><span class="token punctuation">(</span>auto<span class="token punctuation">,</span> auto<span class="token punctuation">)</span> 1fr 1fr<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/qFrOz9Lmxn-892.avif 892w"><source type="image/gif" srcset="https://bitsofco.de/img/qFrOz9Lmxn-892.gif 892w"><img alt="Minmax Function: auto" loading="lazy" decoding="async" src="https://bitsofco.de/img/qFrOz9Lmxn-892.webp" width="892" height="222"></picture></p> <h2 id="using-the-minmax-function-responsive-design-without-media-queries" tabindex="-1">Using the <code>minmax()</code> Function: Responsive Design without Media Queries <a class="header-anchor" href="https://bitsofco.de/how-the-minmax-function-works/">#</a></h2> <p>As we have seen, there are several cases where using the <code>minmax()</code> function is suitable, and we can use it in several ways. But perhaps the most popular and useful case for the <code>minmax()</code> function it's ability to enable us create responsive designs without the use of any media queries.</p> <p>Let's take this example grid:</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/NMGYKnntJm-818.avif 818w"><source type="image/gif" srcset="https://bitsofco.de/img/NMGYKnntJm-818.gif 818w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/NMGYKnntJm-818.webp" width="818" height="352"></picture></p> <p>Each column in the grid has a minimum width of 200px. As the viewport is resized, the number of columns changes to fit their ideal width. With CSS Grid and the <code>minmax()</code> function, this can be created in as little as 2 lines of 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-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>auto-fit<span class="token punctuation">,</span> <span class="token function">minmax</span><span class="token punctuation">(</span>200px<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Besides the <code>minmax()</code> function, there are two other key parts to this:</p> <ul> <li><code>repeat()</code>: This function allows us to specify the same value for multiple columns in the grid. It accepts two values: the number of times to repeat, and the value to be repeated.</li> <li><code>auto-fit</code>: This keyword can be used with the <code>repeat()</code> function instead a number of times to repeat. This will flexibly change the number of columns that are used based on the width each column can be.</li> </ul> <p>One, in my opinion quite major, limitation of this technique, however, is that it only works when each column is of equal width. We have to use the <code>repeat()</code> function with the <code>auto-fit</code> keyword, as this is what allows the number of columns to be flexible. Nonetheless, this can still an extremely useful technique in the right circumstances.</p> CSS Animations vs the Web Animations API: A Case Study 2017-05-03T00:00:00Z https://bitsofco.de/css-animations-vs-the-web-animations-api/ <p>Last week, I wrote about <a href="https://bitsofco.de/how-i-animated-the-bitsofcode-logo">how I created the bitsofcode logo animation with CSS</a>. After that, <a href="https://disq.us/p/1i640o6">it was suggested</a> that I attempt a comparison between a CSS animation and the Web Animations API, so here it is!</p> <h2 id="introduction-to-the-web-animations-api" tabindex="-1">Introduction to the Web Animations API <a class="header-anchor" href="https://bitsofco.de/css-animations-vs-the-web-animations-api/">#</a></h2> <p>As with last week, I'll start this off with an introduction to the Web Animations API. The Web Animations API provides a way for developers to directly manipulate the browser's animation engine using JavaScript.</p> <h3 id="creating-an-animation" tabindex="-1">Creating an Animation <a class="header-anchor" href="https://bitsofco.de/css-animations-vs-the-web-animations-api/">#</a></h3> <p>To create an animation using the Web Animations API, we use the <code>Element.animate()</code> function, which takes two arguments; <code>keyframes</code> and <code>options</code>.</p> <pre class="language-js" tabindex="0"><code class="language-js">element<span class="token punctuation">.</span><span class="token function">animate</span><span class="token punctuation">(</span>keyframes<span class="token punctuation">,</span> options<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <h4 id="keyframes" tabindex="-1"><code>keyframes</code> <a class="header-anchor" href="https://bitsofco.de/css-animations-vs-the-web-animations-api/">#</a></h4> <p>The <code>keyframes</code> object represents the timeline of events in the animation. There are two ways to write this object. To illustrate them, let's use this <code>grow</code> animation, which will scale an element to twice it's size. Here is the animation using CSS <code>@keyframes</code>:</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@keyframes</span> grow</span> <span class="token punctuation">{</span><br> <span class="token selector">0%</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token selector">100%</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>2<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>The first way to write the <code>keyframes</code> is to pass a single object. Each key in the object represents the CSS property we want to animate. The value for each key is an array of CSS values we want to animate through. Each value in the array represents a point in the animation timeline.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> growKeyframes <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'none'</span><span class="token punctuation">,</span> <span class="token string">'scale(2)'</span><span class="token punctuation">]</span><br><span class="token punctuation">}</span></code></pre> <p>The second way to write the <code>keyframes</code> is to write it as an array. Each item in the array represents a point in the timeline, in which we specify each CSS property and value to be applied for that point.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> growKeyframes <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'none'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'scale(2)'</span> <span class="token punctuation">}</span><br><span class="token punctuation">]</span></code></pre> <p>By default, each point on the timeline is equally spaced. For example, if we had 5 points on the timeline, the animation transition between each point will be equally 20% of the animation duration.</p> <p>If we want to alter timing, we can use the <code>offset</code> property with the second format of writing the keyframes. This property accepts a number between 0 and 1, representing the point at which that animation should be. Take, for example, this CSS animation -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@keyframes</span> alteredGrow</span> <span class="token punctuation">{</span><br> <span class="token selector">0%</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span><br> <span class="token selector">10%</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>1.5<span class="token punctuation">)</span> <span class="token punctuation">}</span><br> <span class="token selector">30%</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>1.9<span class="token punctuation">)</span> <span class="token punctuation">}</span><br> <span class="token selector">100%</span> <span class="token punctuation">{</span> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>2<span class="token punctuation">)</span> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p>To account for the uneven spacing in the timeline, we could write it the following way -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> alteredGrowKeyframes <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'none'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'scale(1.5)'</span><span class="token punctuation">,</span> <span class="token literal-property property">offset</span><span class="token operator">:</span> <span class="token number">0.1</span> <span class="token punctuation">}</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'scale(1.9)'</span><span class="token punctuation">,</span> <span class="token literal-property property">offset</span><span class="token operator">:</span> <span class="token number">0.3</span> <span class="token punctuation">}</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'scale(2)'</span> <span class="token punctuation">}</span><br><span class="token punctuation">]</span></code></pre> <h4 id="options" tabindex="-1"><code>options</code> <a class="header-anchor" href="https://bitsofco.de/css-animations-vs-the-web-animations-api/">#</a></h4> <p>The second argument we pass to the <code>animate()</code> function is an object with some options. This object allows us to specify all the properties we would apply to the CSS property being animated if we were using CSS animations. There are 9 options we can specify:</p> <table> <thead> <tr> <th>Option</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>id</code></td> <td>A unique reference for the animation</td> </tr> <tr> <td><code>delay</code></td> <td>Specifies a delay before the animation begins. Corresponds to the <code>animation-delay</code> CSS property</td> </tr> <tr> <td><code>duration</code></td> <td>Specifies the amount of time it should take for one cycle. Corresponds to the <code>animation-duration</code> CSS property</td> </tr> <tr> <td><code>iterations</code></td> <td>Specifies how many times to run a cycle of the animation. Corresponds to the <code>animation-iteration-count</code> CSS property</td> </tr> <tr> <td><code>direction</code></td> <td>Specifies in which direction to run through the animation timeline. Corresponds to the <code>animation-direction</code> CSS property</td> </tr> <tr> <td><code>easing</code></td> <td>Specifies how the animation transitions between steps. Corresponds to the <code>animation-timing-function</code> CSS property</td> </tr> <tr> <td><code>fill</code></td> <td>Specifies the values applied to the element and the start and end of the animation. Corresponds to the <code>animation-fill-mode</code> CSS property</td> </tr> <tr> <td><code>endDelay</code></td> <td>Specifies an amount of time to delay after the end of the animation</td> </tr> <tr> <td><code>iterationStart</code></td> <td>Specifies the point n the iteration the animation should start</td> </tr> </tbody> </table> <p>For example, let's take the <code>alteredGrow</code> animation. With CSS animations, we can apply the animation for 3 seconds, on an infinite loop, alternating directions, after a delay of 2 seconds, with these declarations -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.animated-element</span> <span class="token punctuation">{</span><br> <span class="token property">animation-name</span><span class="token punctuation">:</span> alteredGrow<span class="token punctuation">;</span><br> <span class="token property">animation-duration</span><span class="token punctuation">:</span> 3s<span class="token punctuation">;</span><br> <span class="token property">animation-iteration-count</span><span class="token punctuation">:</span> infinite<span class="token punctuation">;</span><br> <span class="token property">animation-direction</span><span class="token punctuation">:</span> alternate<span class="token punctuation">;</span><br> <span class="token property">animation-delay</span><span class="token punctuation">:</span> 2s<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Using the Web Animations API, we can do the same with the following options -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> alteredGrowOptions <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">duration</span><span class="token operator">:</span> <span class="token number">3000</span><span class="token punctuation">,</span><br> <span class="token literal-property property">iterations</span><span class="token operator">:</span> <span class="token number">Infinity</span><span class="token punctuation">,</span><br> <span class="token literal-property property">direction</span><span class="token operator">:</span> <span class="token string">'alternate'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">delay</span><span class="token operator">:</span> <span class="token number">2000</span><br><span class="token punctuation">}</span></code></pre> <h3 id="using-an-animation" tabindex="-1">Using an Animation <a class="header-anchor" href="https://bitsofco.de/css-animations-vs-the-web-animations-api/">#</a></h3> <p>An animation is applied to an element by calling the <code>animate()</code> function on the element and passing the <code>keyframes</code> and <code>options</code> arguments.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> element <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">'.animated-element'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>element<span class="token punctuation">.</span><span class="token function">animate</span><span class="token punctuation">(</span>alteredGrowKeyframes<span class="token punctuation">,</span> alteredGrowOptions<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>Once that function is called, the animation automatically starts playing. However, we can start and stop it using the <code>play()</code> and <code>pause()</code> methods.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> element <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">'.animated-element'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> myAnimation <span class="token operator">=</span> element<span class="token punctuation">.</span><span class="token function">animate</span><span class="token punctuation">(</span>alteredGrowKeyframes<span class="token punctuation">,</span> alteredGrowOptions<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>myAnimation<span class="token punctuation">.</span><span class="token function">pause</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>myAnimation<span class="token punctuation">.</span><span class="token function">play</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <h3 id="support" tabindex="-1">Support <a class="header-anchor" href="https://bitsofco.de/css-animations-vs-the-web-animations-api/">#</a></h3> <p><a href="https://caniuse.com/#feat=web-animation">Can I Use web-animation?</a> Data on support for the web-animation feature across the major browsers from caniuse.com.</p> <h2 id="animating-the-bitsofcode-logo" tabindex="-1">Animating the bitsofcode Logo <a class="header-anchor" href="https://bitsofco.de/css-animations-vs-the-web-animations-api/">#</a></h2> <p>As with my CSS animation, I recreated a short section of the full animation that was created. Here's a comparison between all three versions -</p> <video width="640" controls="" muted="" playsinline="" poster="https://res.cloudinary.com/ireaderinokun/video/upload/v1493313196/Animating_bitsofcode_pa16yn.jpg"> <source type="video/webm" src="https://res.cloudinary.com/ireaderinokun/video/upload/v1493313196/Animating_bitsofcode_pa16yn.webm"> <img src="https://res.cloudinary.com/ireaderinokun/video/upload/v1493313196/Animating_bitsofcode_pa16yn.jpg"> </video> <h3 id="creating-a-timeline" tabindex="-1">Creating a Timeline <a class="header-anchor" href="https://bitsofco.de/css-animations-vs-the-web-animations-api/">#</a></h3> <p>To recap, here are the animation steps for the left section of the logo (the letters &quot;bitso&quot;, with the opening o).</p> <ol> <li>Move left</li> <li>Return to middle</li> <li>Stay in middle (while waiting for the right section to move right)</li> <li>Move left</li> <li>Rotate</li> <li>Slowly increase rotation</li> <li>Return to unrotated position</li> <li>Return to middle</li> </ol> <p>Based on these steps, this is the timeline I mapped out for the same left section -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/yxV77-u173-960.avif 960w"><source type="image/webp" srcset="https://bitsofco.de/img/yxV77-u173-960.webp 960w"><img alt="Timeline" loading="lazy" decoding="async" src="https://bitsofco.de/img/yxV77-u173-960.png" width="960" height="159"></picture></p> <p>Based on this timeline, here is the <code>keyframes</code> object for the Web Animation, with the styling for each step in the timeline.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> logoSectionLeftKeyframes <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'none'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">offset</span><span class="token operator">:</span> <span class="token number">0.125</span><span class="token punctuation">,</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'translateX(-15px)'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">offset</span><span class="token operator">:</span> <span class="token number">0.25</span><span class="token punctuation">,</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'none'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">offset</span><span class="token operator">:</span> <span class="token number">0.5</span><span class="token punctuation">,</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'none'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">offset</span><span class="token operator">:</span> <span class="token number">0.625</span><span class="token punctuation">,</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'translateX(-15px)'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">offset</span><span class="token operator">:</span> <span class="token number">0.67</span><span class="token punctuation">,</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'translateX(-15px) rotate(-10deg)'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">offset</span><span class="token operator">:</span> <span class="token number">0.72</span><span class="token punctuation">,</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'translateX(-15px) rotate(-10deg)'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">offset</span><span class="token operator">:</span> <span class="token number">0.82</span><span class="token punctuation">,</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'translateX(-15px) rotate(-15deg)'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">offset</span><span class="token operator">:</span> <span class="token number">0.875</span><span class="token punctuation">,</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'translateX(-15px)'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token literal-property property">transform</span><span class="token operator">:</span> <span class="token string">'none'</span> <span class="token punctuation">}</span><br><span class="token punctuation">]</span><span class="token punctuation">;</span></code></pre> <p>Since I needed to use the <code>offset</code> property, I decided to use the array format for the <code>keyframes</code>.</p> <h3 id="setting-the-options" tabindex="-1">Setting the Options <a class="header-anchor" href="https://bitsofco.de/css-animations-vs-the-web-animations-api/">#</a></h3> <p>The options I set for each section were simple; each cycle of the animation should run for 3 secons, looping infinitely.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> logoSectionOptions <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">duration</span><span class="token operator">:</span> <span class="token number">3000</span><span class="token punctuation">,</span><br> <span class="token literal-property property">iterations</span><span class="token operator">:</span> <span class="token number">Infinity</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre> <h3 id="applying-the-animation" tabindex="-1">Applying the Animation <a class="header-anchor" href="https://bitsofco.de/css-animations-vs-the-web-animations-api/">#</a></h3> <p>Applying the animation using the Web Animation API was a lot more fiddly than with CSS. This is because I only wanted the animation to run if the logo was being hovered over or in focus. As I mentioned, by default, web animations run as soon as they are created.</p> <p>To work around this, I had to first create the animation, immediately pause it, then add <code>eventListeners</code> for when I wanted the animation to play or pause. Additionally, since I had to apply an individual animation to each letter, I had to work with several animations at once. Here is how I executed it -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Array to store all animations</span><br><span class="token keyword">const</span> animations <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">function</span> <span class="token function">playLogoAnimation</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> animations<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">animation</span><span class="token punctuation">)</span> <span class="token operator">=></span> animation<span class="token punctuation">.</span><span class="token function">play</span><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">function</span> <span class="token function">pauseLogoAnimation</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> animations<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">animation</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> animation<span class="token punctuation">.</span><span class="token function">pause</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> animation<span class="token punctuation">.</span>currentTime <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">// Reset animation to start state</span><br> <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">createLogoAnimation</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> logoSectionLeftEls <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">'.logo-section-left'</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span><br> logoSectionLeftEls<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">el</span><span class="token punctuation">)</span> <span class="token operator">=></span> animations<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>el<span class="token punctuation">.</span><span class="token function">animate</span><span class="token punctuation">(</span>logoSectionLeftKeyframes<span class="token punctuation">,</span> logoSectionTiming<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><br><br> <span class="token comment">// Animation for middle and right sections here …</span><br><br> <span class="token comment">// Immediately pause animation once created</span><br> <span class="token function">pauseLogoAnimation</span><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 function">createLogoAnimation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Event listeners to play or pause animation</span><br><span class="token keyword">const</span> siteTitleLink <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">'.site__title a'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>siteTitleLink<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'mouseover'</span><span class="token punctuation">,</span> playLogoAnimation<span class="token punctuation">)</span><span class="token punctuation">;</span><br>siteTitleLink<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'mouseout'</span><span class="token punctuation">,</span> pauseLogoAnimation<span class="token punctuation">)</span><span class="token punctuation">;</span><br>siteTitleLink<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 punctuation">)</span> <span class="token function">playLogoAnimation</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>siteTitleLink<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'keydown'</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 punctuation">)</span> <span class="token function">pauseLogoAnimation</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>Here's the CodePen with the full animation:</p> <p>See the Pen <a href="https://codepen.io/ire/pen/MmJOzR/">MmJOzR</a> by Ire Aderinokun (<a href="https://codepen.io/ire">@ire</a>) on <a href="https://codepen.io">CodePen</a>.</p> <h2 id="css-animations-vs-the-web-animation-api" tabindex="-1">CSS Animations vs the Web Animation API <a class="header-anchor" href="https://bitsofco.de/css-animations-vs-the-web-animations-api/">#</a></h2> <p>As with everything, whether to use CSS or JavaScript animations, depends very much on the particulars of the animation. As a general rule, CSS animations should be used for small, UI-related animations such as showing a tooltip. The Web Animation API should be reserved for more advanced effects that need fine-tuned control. That said, here was my comparison between the two methods for this animation.</p> <h3 id="performance" tabindex="-1">Performance <a class="header-anchor" href="https://bitsofco.de/css-animations-vs-the-web-animations-api/">#</a></h3> <p>The performance of CSS vs JavaScript animations can vary a lot depending on which properties we are animating. Generally, it is advised to only animate the <code>transform</code> and/or <code>opacity</code> properties, as these animations can be executed on a different thread from the browser's main thread.</p> <blockquote> <p>Changing `transform` does not trigger any geometry changes or painting, which is very good. This means that the operation can likely be carried out by the compositor thread with the help of the GPU.</p> <p><cite><a href="https://csstriggers.com/transform">CSS Triggers</a></cite></p> </blockquote> <p>Since my animations only used the <code>transform</code> property, I was not able to see any significant difference in performance between the two methods. Using <a href="https://developer.mozilla.org/en-US/Apps/Fundamentals/Performance/CSS_JavaScript_animation_performance">Firefox's DevTools</a>, I measured the frame rate of both animations and got the exact same rate of 60 FPS, even with Off Main Thread Animation enabled.</p> <p>I wasn't able to find more ways to measure the performance between the two versions. If you know of any better ways, do leave a comment below.</p> <h3 id="developer-experience" tabindex="-1">Developer Experience <a class="header-anchor" href="https://bitsofco.de/css-animations-vs-the-web-animations-api/">#</a></h3> <p>In this case, I personally found the CSS animation easier to work with than the Web Animation API, mainly because of the extra work it took to get the animation to be played/paused using the latter. If I were doing a more complex animation, for example for a game, the Web Animation API would definitely be the way to go. However, for this case, I think the CSS animation was simpler to implement.</p> How I Animated the bitsofcode Logo with CSS 2017-04-26T00:00:00Z https://bitsofco.de/how-i-animated-the-bitsofcode-logo/ <p>I’m pretty new to CSS animations. For the most part, I had only used them in limited cases and mostly using libraries created by others, such as the excellent <a href="https://daneden.github.io/animate.css/">Animate.css</a> created by <a href="https://daneden.me/">Daniel Eden</a>.</p> <p>As I mentioned in <a href="https://bitsofco.de/redesigning-bitsofcode-3">Redesigning bitsofcode</a>, the graphic designer I worked with for the new bitsofcode logo created this animation to go with it.</p> <video width="640" controls="" muted="" playsinline="" poster="https://bitsofco.de/content/images/2017/03/Logo-Redesign.png"> <source type="video/webm" src="https://res.cloudinary.com/ireaderinokun/video/upload/c_scale,w_640/v1490346377/Updated_Animation_for_bitsofco.de_-_9th_March_2017_yh6s0n.webm"> <img src="https://bitsofco.de/content/images/2017/03/Logo-Redesign.png"> </video> <p>I knew I had to use it somewhere, so I decided to attempt recreating a section of it as a pure CSS animation.</p> <h2 id="introduction-to-css-animations" tabindex="-1">Introduction to CSS Animations <a class="header-anchor" href="https://bitsofco.de/how-i-animated-the-bitsofcode-logo/">#</a></h2> <p>Before getting into how I created the logo animation, it'll be helpful to give a bit of an introduction to how CSS animations work. CSS animations make it possible for us to apply custom, complex animations to elements without needing to use additional tools or languages like JavaScript. They have a lot of benefits, such as being simpler to use and having better performance than other methods.</p> <h3 id="creating-an-animation" tabindex="-1">Creating an Animation <a class="header-anchor" href="https://bitsofco.de/how-i-animated-the-bitsofcode-logo/">#</a></h3> <p>To create a CSS animation, we use the <code>@keyframes</code> rule, with which we can name our animation and specify the CSS styles to be applied to the animating element at each step of the animation. Take, for example, the following animation, named <code>grow</code>, which will scale an element to twice it’s size:</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@keyframes</span> grow</span> <span class="token punctuation">{</span><br> <span class="token selector">0%</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token selector">100%</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>2<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>When creating a custom animation, the animation name is defined after the <code>@keyframes</code> keyword and the animation steps are defined within the block. The steps in an animation can be thought of as a timeline, from 0, marking the start of the animation, to 100, marking the end of the animation.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/TaQM9LUwa--960.avif 960w"><source type="image/webp" srcset="https://bitsofco.de/img/TaQM9LUwa--960.webp 960w"><img alt="Timeline" loading="lazy" decoding="async" src="https://bitsofco.de/img/TaQM9LUwa--960.png" width="960" height="122"></picture></p> <p>Besides those two points on the timeline, we can have as many steps in between as we like. For example, let’s adapt the <code>grow</code> animation from above to a <code>pulse</code>. Instead of simply enlarging the element, we also return it back to its original size.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@keyframes</span> pulse</span> <span class="token punctuation">{</span><br> <span class="token selector">0%</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token selector">50%</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>2<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token selector">100%</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p>We also don’t have to stick to one CSS style for each point on the timeline. We can apply as many styles as we want, like we apply styles to a selector.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@keyframes</span> pulseAndChangeColour</span> <span class="token punctuation">{</span><br> <span class="token selector">0%</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> none<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><br> <span class="token selector">25%</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> <span class="token selector">50%</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>2<span class="token punctuation">)</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> <span class="token selector">75%</span> <span class="token punctuation">{</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> pink<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token selector">100%</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> orange<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <h3 id="using-an-animation" tabindex="-1">Using an Animation <a class="header-anchor" href="https://bitsofco.de/how-i-animated-the-bitsofcode-logo/">#</a></h3> <p>Once we have created the animation, we can then apply it to any element using the <code>animation</code> properties. There are nine properties related to applying CSS animations.</p> <table> <thead> <tr> <th>Property</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>animation-name</code></td> <td>Specifies the name of the <code>@keyframes</code> rule to apply</td> </tr> <tr> <td><code>animation-delay</code></td> <td>Specifies a delay before the animation begins</td> </tr> <tr> <td><code>animation-duration</code></td> <td>Specifies the amount of time it should take for one cycle</td> </tr> <tr> <td><code>animation-iteration-count</code></td> <td>Specifies how many times to run a cycle of the animation</td> </tr> <tr> <td><code>animation-direction</code></td> <td>Specifies in which direction to run through the animation timeline</td> </tr> <tr> <td><code>animation-play-state</code></td> <td>Determines if the animation is playing or paused</td> </tr> <tr> <td><code>animation-timing-function</code></td> <td>Specifies how the animation transitions between steps</td> </tr> <tr> <td><code>animation-fill-mode</code></td> <td>Specifies the values applied to the element and the start and end of the animation</td> </tr> <tr> <td><code>animation</code></td> <td>Shorthand for all other properties</td> </tr> </tbody> </table> <p>For example, we can apply our <code>pulseAndChangeColour</code> animation for 3 seconds, on an infinite loop, alternating directions, after a delay of 2 seconds, with these declarations -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.animated-element</span> <span class="token punctuation">{</span><br> <span class="token property">animation-name</span><span class="token punctuation">:</span> pulseAndChangeColour<span class="token punctuation">;</span><br> <span class="token property">animation-duration</span><span class="token punctuation">:</span> 3s<span class="token punctuation">;</span><br> <span class="token property">animation-iteration-count</span><span class="token punctuation">:</span> infinite<span class="token punctuation">;</span><br> <span class="token property">animation-direction</span><span class="token punctuation">:</span> alternate<span class="token punctuation">;</span><br> <span class="token property">animation-delay</span><span class="token punctuation">:</span> 2s<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h3 id="support" tabindex="-1">Support <a class="header-anchor" href="https://bitsofco.de/how-i-animated-the-bitsofcode-logo/">#</a></h3> <p class="ciu_embed" data-feature="css-animation" 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-animation.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/css-animation.png"> <img src="https://caniuse.bitsofco.de/image/css-animation.jpg" alt="Data on support for the css-animation feature across the major browsers from caniuse.com"> </picture> </p> <h2 id="animating-the-bitsofcode-logo" tabindex="-1">Animating the bitsofcode Logo <a class="header-anchor" href="https://bitsofco.de/how-i-animated-the-bitsofcode-logo/">#</a></h2> <p>In my CSS animation, I decided to recreate just a section of the full animation that was created. Here's my recreation of that section -</p> <video width="640" controls="" muted="" playsinline="" poster="https://bitsofco.de/content/images/2017/03/Logo-Redesign.png"> <source type="video/webm" src="https://res.cloudinary.com/ireaderinokun/video/upload/v1493138919/css_animation_oph4hr.webm"> <img src="https://bitsofco.de/content/images/2017/03/Logo-Redesign.png"> </video> <h3 id="prepping-the-svg-element" tabindex="-1">Prepping the SVG Element <a class="header-anchor" href="https://bitsofco.de/how-i-animated-the-bitsofcode-logo/">#</a></h3> <p>The logo, as you would imagine, is an SVG element. Each letter (or, in the case of the letter &quot;o&quot;, half of a letter), is its own <code>&lt;path&gt;</code>. This allowed me to target each letter individually or target specific groups of letters together.</p> <p>Based on the animation, there are three discernible groups of letters, which I grouped together using three classes:</p> <ul> <li><code>.logo-section—left</code>: The letters &quot;bitso&quot; (with the opening o)</li> <li><code>.logo-section—middle</code>: The letters &quot;ofco&quot; (with the closing o and opening o)</li> <li><code>.logo-section—right</code>: The letters &quot;ode&quot; (with the closing o)</li> </ul> <p>A separate <code>@keyframes</code> rule was created for each of those sections, as the animation is slightly different for each of them.</p> <h3 id="creating-a-timeline" tabindex="-1">Creating a Timeline <a class="header-anchor" href="https://bitsofco.de/how-i-animated-the-bitsofcode-logo/">#</a></h3> <p>As I mentioned, each CSS animation has a timeline of events, from 0% to 100%. Since this was a bit more complex an animation than the <code>pulse</code> examples, I found it helpful to specifically write out the timeline before writing any CSS.</p> <p>Let’s take the animation for the left section of the logo. These were the basic steps -</p> <ol> <li>Move left</li> <li>Return to middle</li> <li>Stay in middle (while waiting for the right section to move right)</li> <li>Move left</li> <li>Rotate</li> <li>Slowly increase rotation</li> <li>Return to unrotated position</li> <li>Return to middle</li> </ol> <p>Next, I had to takes these steps and map them onto a timeline, from 0 to 100%, where each step should be.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/yxV77-u173-960.avif 960w"><source type="image/webp" srcset="https://bitsofco.de/img/yxV77-u173-960.webp 960w"><img alt="Timeline" loading="lazy" decoding="async" src="https://bitsofco.de/img/yxV77-u173-960.png" width="960" height="159"></picture></p> <p>This could then be translated into an actual CSS animation timeline.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@keyframes</span> logoSectionLeft</span> <span class="token punctuation">{</span><br> <span class="token selector">0%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* Initial Position */</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">12.5%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 1. Move Left */</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">25%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 2. Return to middle */</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">50%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 3. Stay in middle (while waiting for the right section to move right) */</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">62.5%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 4. Move left */</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">67%,<br> 72%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 5. Rotate */</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">82%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 6. Slowly increase rotation */</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">87.5%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 7. Return to unrotated position */</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">100%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 8. Return to middle */</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <h3 id="styling-each-step" tabindex="-1">Styling each Step <a class="header-anchor" href="https://bitsofco.de/how-i-animated-the-bitsofcode-logo/">#</a></h3> <p>Once the timeline had been mapped out, I could then add the CSS styles for each step. For this animation, the only property I used was the <code>transform</code> property with <code>translate()</code> and <code>rotate()</code> functions.</p> <p>Here’s the complete animation keyframes for the left section -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@keyframes</span> logoSectionLeft</span> <span class="token punctuation">{</span><br> <span class="token selector">0%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* Initial Position */</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">12.5%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 1. Move Left */</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateX</span><span class="token punctuation">(</span>-15px<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">25%,<br> 50%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 2. Return to middle */</span><br> <span class="token comment">/* 3. Stay in middle (while waiting for the right section to move right) */</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">62.5%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 4. Move left */</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateX</span><span class="token punctuation">(</span>-15px<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">67%,<br> 72%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 5. Rotate */</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateX</span><span class="token punctuation">(</span>-15px<span class="token punctuation">)</span> <span class="token function">rotate</span><span class="token punctuation">(</span>-10deg<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">82%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 6. Slowly increase rotation */</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateX</span><span class="token punctuation">(</span>-15px<span class="token punctuation">)</span> <span class="token function">rotate</span><span class="token punctuation">(</span>-15deg<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">87.5%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 7. Return to unrotated position */</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateX</span><span class="token punctuation">(</span>-15px<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token selector">100%</span> <span class="token punctuation">{</span><br> <span class="token comment">/* 7. Return to middle */</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <h3 id="applying-the-animation" tabindex="-1">Applying the Animation <a class="header-anchor" href="https://bitsofco.de/how-i-animated-the-bitsofcode-logo/">#</a></h3> <p>Finally, I wanted to apply the animation only when the anchor element surrounding the logo was in focus or hovered above. I wanted a cycle of the animation to take 3 seconds, and for it to repeat indefinitely.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.site__title a:hover .logo-section-left,<br>.site__title a:focus .logo-section-left</span> <span class="token punctuation">{</span><br> <span class="token property">animation-name</span><span class="token punctuation">:</span> logoSectionLeft<span class="token punctuation">;</span><br> <span class="token property">animation-duration</span><span class="token punctuation">:</span> 3s<span class="token punctuation">;</span><br> <span class="token property">animation-timing-function</span><span class="token punctuation">:</span> ease-in-out<span class="token punctuation">;</span><br> <span class="token property">animation-iteration-count</span><span class="token punctuation">:</span> infinite<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>That’s it! I created a CodePen with the full animation if you want to take a look.</p> <p>See the Pen <a href="https://codepen.io/ire/pen/oWbLdQ/">bitsofcode</a> by Ire Aderinokun (<a href="https://codepen.io/ire">@ire</a>) on <a href="https://codepen.io">CodePen</a>.</p> Asynchronous Functions 101 2017-04-13T00:00:00Z https://bitsofco.de/asynchronous-functions-101/ <p>One of the major advantages of JavaScript is that everything is asynchronous. For the most part, different parts of your code don’t affect the execution of others.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token function">doALongThing</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"I will be logged second!"</span><span class="token punctuation">)</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">"I will be logged first!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>Unfortunately, this is also one of JavaScript's major disadvantages. Because everything is asynchronous by default, it makes it that much more difficult when you <em>do</em> want to execute code synchronously.</p> <p>The first solution to this problem was callbacks. If a part of your code was dependent on the result of something else, we would have to nest our code -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token function">doSomething</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 function">doSomethingElse</span><span class="token punctuation">(</span>response<span class="token punctuation">,</span><span class="token punctuation">(</span><span class="token parameter">secondResponse</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token function">doAThirdThing</span><span class="token punctuation">(</span>secondResponse<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></code></pre> <p>Nesting callbacks on callbacks, as we know, became unmaintainable. So, the solution of promises was created. This allowed us to deal with synchronous code in a much cleaner, flatter, way.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token function">doSomething</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">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">doSomethingElse</span><span class="token punctuation">(</span>response<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">secondResponse</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">doAThirdThing</span><span class="token punctuation">(</span>secondResponse<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Even cleaner</span><br><span class="token function">doSomething</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>doSomethingElse<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>doAThirdThing<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>As with everything, promises were not perfect either. So, as part of the ES2017 Specification, another method for dealing with synchronous code was defined; asynchronous functions. These allow us to write asynchronous code as if it were synchronous.</p> <h2 id="creating-an-asynchronous-function" tabindex="-1">Creating an Asynchronous Function <a class="header-anchor" href="https://bitsofco.de/asynchronous-functions-101/">#</a></h2> <p>An asynchronous function is defined with the <code>async</code> function expression. A basic function looks like this -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> value <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">somePromise</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> value<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>We define a function as an asynchronous function by preceding the function declaration with <code>async</code>. This keyword can be used with any function declaration syntax -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Basic function</span><br><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> … <span class="token punctuation">}</span><br><br><span class="token comment">// Arrow function</span><br><span class="token keyword">const</span> <span class="token function-variable function">foo</span> <span class="token operator">=</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> … <span class="token punctuation">}</span><br><br><span class="token comment">// Class methods</span><br><span class="token keyword">class</span> <span class="token class-name">Bar</span> <span class="token punctuation">{</span><br> <span class="token keyword">async</span> <span class="token function">foo</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></code></pre> <p>Once we have defined a function as an asynchronous function, we are able to use the <code>await</code> keyword. This keyword is placed before the calling of a promise, which will pause the execution of the function until the promise is either fulfilled or rejected.</p> <h2 id="handling-errors" tabindex="-1">Handling Errors <a class="header-anchor" href="https://bitsofco.de/asynchronous-functions-101/">#</a></h2> <p>Error handling in asynchronous functions is done with the help of <code>try</code> and <code>catch</code> blocks. The first block, <code>try</code>, allows us to attempt an action. The second block, <code>catch</code>, is called if the action is failed. It accepts one paramter containing whatever error was thrown.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">try</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> value <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">somePromise</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> value<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token keyword">catch</span> <span class="token punctuation">(</span>err<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">"Oops, there was an error :("</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <h2 id="using-an-asynchronous-function" tabindex="-1">Using an Asynchronous Function <a class="header-anchor" href="https://bitsofco.de/asynchronous-functions-101/">#</a></h2> <p>Asynchronous functions aren't really a replacement for promises. The two work hand in hand. An asynchronous function will <code>await</code> the execution of a promise, and an asynchronous function will always <em>return</em> a promise.</p> <p>The promise returned by an asynchronous function will resolve with whatever value is returned by the function.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">await</span> <span class="token function">somePromise</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> ‘success<span class="token operator">!</span>’<br><span class="token punctuation">}</span><br><br><span class="token function">foo</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">res</span><span class="token punctuation">)</span> <span class="token operator">=></span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>res<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// ‘success!’</span></code></pre> <p>If an error is thrown, the Promise will be rejected with that error.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">await</span> <span class="token function">somePromise</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">throw</span> <span class="token function">Error</span><span class="token punctuation">(</span>‘oops<span class="token operator">!</span>’<span class="token punctuation">)</span><br><span class="token punctuation">}</span><br><br><span class="token function">foo</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">res</span><span class="token punctuation">)</span> <span class="token operator">=></span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>res<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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// ‘oops!’</span></code></pre> <h2 id="executing-asynchronous-functions-in-parallel" tabindex="-1">Executing Asynchronous Functions in Parallel <a class="header-anchor" href="https://bitsofco.de/asynchronous-functions-101/">#</a></h2> <p>With promises, we can execute multiple promises in parallel using the <code>Promise.all()</code> method.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">pause500ms</span><span class="token punctuation">(</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">res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">setTimeout</span><span class="token punctuation">(</span>res<span class="token punctuation">,</span> <span class="token number">500</span><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">const</span> promise1 <span class="token operator">=</span> <span class="token function">pause500ms</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> promise2 <span class="token operator">=</span> <span class="token function">pause500ms</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>Promise<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">[</span>promise1<span class="token punctuation">,</span> promise2<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 punctuation">)</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 string">"I will be logged after 500ms"</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>With asynchronous functions, we have to work around a bit to get the same effect. If we just list each function to await in sequence, they will be executed in sequence since <code>await</code> will pause the execution of the rest of the function.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">inSequence</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">await</span> <span class="token function">pause500ms</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">await</span> <span class="token function">pause500ms</span><span class="token punctuation">(</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">"I will be logged after 1000ms"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>This will take 1000ms to complete, because the second <code>await</code> is not even started until the first has been completed. To get around this, we need to reference the functions in this way -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">inParallel</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> await1 <span class="token operator">=</span> <span class="token function">pause500ms</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> await2 <span class="token operator">=</span> <span class="token function">pause500ms</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">await</span> await1<span class="token punctuation">;</span><br> <span class="token keyword">await</span> await2<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">"I will be logged after 500ms"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>This will take only 500ms to complete because both <code>pause500ms()</code> functions are being executed at the same time.</p> <h2 id="promises-or-asynchronous-function" tabindex="-1">Promises or Asynchronous Function? <a class="header-anchor" href="https://bitsofco.de/asynchronous-functions-101/">#</a></h2> <p>As I mentioned, asynchronous functions aren't a replacement for promises, the two are used together. Asynchronous functions provide an alternative, and in some cases better, way of working with promise-based functions. But, they still use and produce promises.</p> <p>Since a promise is returned, an asynchronous function can be called by another asynchronous function, or a promise. We can mix and match depending on which syntax is best suited for each case.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">baz</span><span class="token punctuation">(</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">res</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">setTimeout</span><span class="token punctuation">(</span>res<span class="token punctuation">,</span> <span class="token number">1000</span><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">async</span> <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">await</span> <span class="token function">baz</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> <span class="token string">'foo complete!'</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">bar</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> value <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">foo</span><span class="token punctuation">(</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>value<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> <span class="token string">'bar complete!'</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token function">bar</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">value</span><span class="token punctuation">)</span> <span class="token operator">=></span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>The following will occur:</p> <ol> <li>Wait 1000ms</li> <li>Log &quot;foo complete!&quot;</li> <li>Log &quot;bar complete!&quot;</li> </ol> <h2 id="support" tabindex="-1">Support <a class="header-anchor" href="https://bitsofco.de/asynchronous-functions-101/">#</a></h2> <p>At the time of writing, both asynchronous functions and promises are available in the current versions of all major browsers, with the exception of Internet Explorer and Opera Mini.</p> <p><a href="https://caniuse.com/#feat=promises"><picture><source type="image/avif" srcset="https://bitsofco.de/img/q-ewOppA8s-1616.avif 1616w"><source type="image/webp" srcset="https://bitsofco.de/img/q-ewOppA8s-1616.webp 1616w"><img alt="Can I Use promises" loading="lazy" decoding="async" src="https://bitsofco.de/img/q-ewOppA8s-1616.png" width="1616" height="810"></picture></a></p> <p><a href="https://caniuse.com/#feat=async-functions"><picture><source type="image/avif" srcset="https://bitsofco.de/img/vNHCBL8QOq-1612.avif 1612w"><source type="image/webp" srcset="https://bitsofco.de/img/vNHCBL8QOq-1612.webp 1612w"><img alt="Can I Use async-functions" loading="lazy" decoding="async" src="https://bitsofco.de/img/vNHCBL8QOq-1612.png" width="1612" height="806"></picture></a></p> Redesigning bitsofcode 2017-03-27T00:00:00Z https://bitsofco.de/redesigning-bitsofcode-3/ <p>Welcome to the new bitsofcode! Since launching this blog over 2 years ago, I’ve had two designs, this being the third.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/kAWAmX4EIA-640.avif 640w"><source type="image/webp" srcset="https://bitsofco.de/img/kAWAmX4EIA-640.webp 640w"><img alt="First and second bitsofcode designs" loading="lazy" decoding="async" src="https://bitsofco.de/img/kAWAmX4EIA-640.png" width="640" height="387"></picture></p> <p>You can check out the <a href="https://web.archive.org/web/20150602223245/https://bitsofco.de/">first</a> and <a href="https://web.archive.org/web/20160502162314/https://bitsofco.de/">second</a> designs using the <a href="https://web.archive.org/web/20150101000000*/https://bitsofco.de">WayBack Machine</a>. The first one looks particularly embarrassing... Anyway, I thought I would share here my process in this redesign and rebuild.</p> <h2 id="a-logo-finally" tabindex="-1">A Logo (Finally) <a class="header-anchor" href="https://bitsofco.de/redesigning-bitsofcode-3/">#</a></h2> <p>Over the past two years, I haven’t really had a solid visual identity for bitsofcode. Besides the yellow colour, there hasn’t been any other identifiable asset for the blog. This hasn't been for lack of trying, I had attempted to design a logo myself on several occasions, but never ended up with anything I actually liked. In the end, I just decided to hire someone, in this case the amazing designer <a href="https://www.dadesignstudio.net">Seyi Olusanya</a>, and the bitsofcode logo was born.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/z5hjOO1IjR-640.avif 640w"><source type="image/webp" srcset="https://bitsofco.de/img/z5hjOO1IjR-640.webp 640w"><img alt="bitsofcode logo" loading="lazy" decoding="async" src="https://bitsofco.de/img/z5hjOO1IjR-640.png" width="640" height="190"></picture></p> <p>I think Seyi did really well in embodying the personality of the blog. He also had the idea to create a fun animation with the logo, and came up with this -</p> <video width="640" controls="" muted="" playsinline="" poster="https://bitsofco.de/content/images/2017/03/Logo-Redesign.png"> <source type="video/webm" src="https://res.cloudinary.com/ireaderinokun/video/upload/c_scale,w_640/v1490346377/Updated_Animation_for_bitsofco.de_-_9th_March_2017_yh6s0n.webm"> <img src="https://bitsofco.de/content/images/2017/03/Logo-Redesign.png"> </video> <p>I decided to implement a part of that video as a CSS animation. If you haven't already noticed, the animation is activated when users interact with the logo in the header. I plan on writing about how I created the CSS animation in more detail in a later article.</p> <h2 id="design-decisions" tabindex="-1">Design Decisions <a class="header-anchor" href="https://bitsofco.de/redesigning-bitsofcode-3/">#</a></h2> <p>Despite the resultantly simple design, I spent a lot of time on the redesign. There were a couple decisions I particularly dwelled on.</p> <h3 id="how-to-use-the-theme-colour" tabindex="-1">How to use the theme colour? <a class="header-anchor" href="https://bitsofco.de/redesigning-bitsofcode-3/">#</a></h3> <p>In my previous design, I had used the theme colour, <code>#FFDB3C</code>, a lot more, particularly on the home page. In this design, I wanted the yellow to be more of an accent, rather than a primary colour.</p> <p>I played around with many different ways of having the yellow be an accent, before eventually settling with having a yellow border around the entire page, and using it for highlights in certain cases. I also created a custom code highlighting theme, in which I made use of the yellow as the primary colour.</p> <h3 id="to-hamburger-or-not-to-hamburger" tabindex="-1">To hamburger or not to hamburger? <a class="header-anchor" href="https://bitsofco.de/redesigning-bitsofcode-3/">#</a></h3> <p>How to handle the navigation was a question I fought with for a while. I had used the hamburger menu in my last design, mainly because the navigation wasn't that important. The most important item on the menu was home, which could also be accessed through the logo.</p> <p>I decided to use the hamburger menu again for a few reasons. First, like in the previous design, the contents of the menu don't seem <em>that</em> important. People access articles through the page content, not through the menu. Second, I felt like the other design decisions I had made left it the best choice. For example, I wanted the logo to be in the middle, which didn't leave much space for a full menu in the header as well.</p> <p>So, although the hamburger menu definitely has it's shortcomings, I felt it was the best option for this particular case.</p> <h2 id="performance-improvements" tabindex="-1">Performance Improvements <a class="header-anchor" href="https://bitsofco.de/redesigning-bitsofcode-3/">#</a></h2> <p>A major reason I wanted to redesign (and, more importantly, rebuild) the site was because I felt I could make a lot of improvements performance-wise. Over the past year in particular, I've researched into and written a decent amount about performance, yet hadn't really implemented that knowledge on this site itself.</p> <p>Overall, the changes I made have been a decent improvement. My score on <a href="https://developers.google.com/speed/pagespeed/insights">PageSpeed Insights</a> went from <strong>77</strong> to <strong>97</strong> on Desktop (70 to 96 on Mobile).</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/lgRYd57X16-640.avif 640w"><source type="image/webp" srcset="https://bitsofco.de/img/lgRYd57X16-640.webp 640w"><img alt="PageSpeed Insights Score on the previous design compared with the current" loading="lazy" decoding="async" src="https://bitsofco.de/img/lgRYd57X16-640.png" width="640" height="387"></picture></p> <p>To achieve this, I made the following optimisations.</p> <h3 id="use-system-fonts" tabindex="-1">Use System Fonts <a class="header-anchor" href="https://bitsofco.de/redesigning-bitsofcode-3/">#</a></h3> <p>In my last design, 33.8% of bytes downloaded were from fonts alone. This was clearly an area ripe for improvement. In this revamp, I cut that down to zero by <a href="https://bitsofco.de/using-system-fonts-in-the-browser">using System Fonts</a> instead. For the Sans-Serif font, which is used for the majority of the site, I used the same system font stack used by Wordpress -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">p</span> <span class="token punctuation">{</span><br> <span class="token property">font-family</span><span class="token punctuation">:</span> -apple-system<span class="token punctuation">,</span><br> BlinkMacSystemFont<span class="token punctuation">,</span><br> <span class="token string">"Segoe UI"</span><span class="token punctuation">,</span><br> Roboto<span class="token punctuation">,</span><br> Oxygen-Sans<span class="token punctuation">,</span><br> Ubuntu<span class="token punctuation">,</span><br> Cantarell<span class="token punctuation">,</span><br> <span class="token string">"Helvetica Neue"</span><span class="token punctuation">,</span><br> sans-serif<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>For the Monospace font used in code samples, I put together my own stack based on fonts I liked -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">code</span> <span class="token punctuation">{</span><br> <span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">"Inconsolata"</span><span class="token punctuation">,</span><br> Consolas<span class="token punctuation">,</span><br> Monaco<span class="token punctuation">,</span><br> <span class="token string">"Andale Mono"</span><span class="token punctuation">,</span><br> <span class="token string">"Ubuntu Mono"</span><span class="token punctuation">,</span><br> monospace<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>This is what a typical article looks like on Mac versus Windows -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/k2EneBtzs1-640.avif 640w"><source type="image/webp" srcset="https://bitsofco.de/img/k2EneBtzs1-640.webp 640w"><img alt="System fonts used on Windows 10 versus macOS 10" loading="lazy" decoding="async" src="https://bitsofco.de/img/k2EneBtzs1-640.png" width="640" height="408"></picture></p> <h3 id="reduce-assets-dependencies" tabindex="-1">Reduce Assets / Dependencies <a class="header-anchor" href="https://bitsofco.de/redesigning-bitsofcode-3/">#</a></h3> <p>In addition to cutting out Google fonts, I was able to improve performance by cutting down on assets I didn’t really need to be using. In particular, I was able to cut the following:</p> <ul> <li><strong>Font Awesome Icon Fonts</strong>: Instead, I use SVG for all icons.</li> <li><strong>jQuery</strong>: I was barely even using jQuery in the previous design, so this was pretty easy to cut out and reimplement the logic in plain JavaScript.</li> </ul> <p>On the flipside, I had to add some new dependencies due to the addition of Carbon ads on the site. However, there was still a net decrease in assets being loaded.</p> <h3 id="optimize-css-delivery" tabindex="-1">Optimize CSS Delivery <a class="header-anchor" href="https://bitsofco.de/redesigning-bitsofcode-3/">#</a></h3> <p>A major improvement I made was to optimise the delivery of the main styles of the page. A pretty common “Should Fix” message many sites get on <a href="https://developers.google.com/speed/pagespeed/insights">PageSpeed Insights</a> is the following:</p> <blockquote> <p><strong>Eliminate render-blocking JavaScript and CSS in above-the-fold content</strong>. None of the above-the-fold content on your page could be rendered without waiting for the following resources to load. Try to defer or asynchronously load blocking resources, or inline the critical portions of those resources directly in the HTML</p> </blockquote> <p>The way to fix this is by having some critical CSS directly in our document <code>&lt;head&gt;</code>. Then, once the page has been loaded and is rendering on the page, use JavaScript to fetch and insert the full CSS file. I used the <a href="https://github.com/filamentgroup/loadCSS">loadCSS</a> library to help do this. To generate the critical CSS, I used a postCSS plugin I created and am testing out (more on that later!).</p> <p>The <code>&lt;head&gt;</code> of the document now looks something 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">&lt;</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">&lt;</span>title</span><span class="token punctuation">></span></span>bitsofcode<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span><br><br> <span class="token comment">&lt;!-- loadCSS library --></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br> <span class="token comment">// https://github.com/filamentgroup/loadCSS</span><br> <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><span class="token string">"use strict"</span><span class="token punctuation">;</span><span class="token keyword">var</span> <span class="token function-variable function">n</span><span class="token operator">=</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">n<span class="token punctuation">,</span>t<span class="token punctuation">,</span>o</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">function</span> <span class="token function">i</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">return</span> a<span class="token punctuation">.</span>body<span class="token operator">?</span><span class="token function">e</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span><span class="token keyword">void</span> <span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token function">i</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">function</span> <span class="token function">r</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>l<span class="token punctuation">.</span>addEventListener<span class="token operator">&amp;&amp;</span>l<span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">"load"</span><span class="token punctuation">,</span>r<span class="token punctuation">)</span><span class="token punctuation">,</span>l<span class="token punctuation">.</span>media<span class="token operator">=</span>o<span class="token operator">||</span><span class="token string">"all"</span><span class="token punctuation">}</span><span class="token keyword">var</span> d<span class="token punctuation">,</span>a<span class="token operator">=</span>e<span class="token punctuation">.</span>document<span class="token punctuation">,</span>l<span class="token operator">=</span>a<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">"link"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">if</span><span class="token punctuation">(</span>t<span class="token punctuation">)</span>d<span class="token operator">=</span>t<span class="token punctuation">;</span><span class="token keyword">else</span><span class="token punctuation">{</span><span class="token keyword">var</span> s<span class="token operator">=</span><span class="token punctuation">(</span>a<span class="token punctuation">.</span>body<span class="token operator">||</span>a<span class="token punctuation">.</span><span class="token function">getElementsByTagName</span><span class="token punctuation">(</span><span class="token string">"head"</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><span class="token punctuation">.</span>childNodes<span class="token punctuation">;</span>d<span class="token operator">=</span>s<span class="token punctuation">[</span>s<span class="token punctuation">.</span>length<span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">}</span><span class="token keyword">var</span> f<span class="token operator">=</span>a<span class="token punctuation">.</span>styleSheets<span class="token punctuation">;</span>l<span class="token punctuation">.</span>rel<span class="token operator">=</span><span class="token string">"stylesheet"</span><span class="token punctuation">,</span>l<span class="token punctuation">.</span>href<span class="token operator">=</span>n<span class="token punctuation">,</span>l<span class="token punctuation">.</span>media<span class="token operator">=</span><span class="token string">"only x"</span><span class="token punctuation">,</span><span class="token function">i</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>d<span class="token punctuation">.</span>parentNode<span class="token punctuation">.</span><span class="token function">insertBefore</span><span class="token punctuation">(</span>l<span class="token punctuation">,</span>t<span class="token operator">?</span>d<span class="token operator">:</span>d<span class="token punctuation">.</span>nextSibling<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">var</span> <span class="token function-variable function">u</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><span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">var</span> n<span class="token operator">=</span>l<span class="token punctuation">.</span>href<span class="token punctuation">,</span>t<span class="token operator">=</span>f<span class="token punctuation">.</span>length<span class="token punctuation">;</span>t<span class="token operator">--</span><span class="token punctuation">;</span><span class="token punctuation">)</span><span class="token keyword">if</span><span class="token punctuation">(</span>f<span class="token punctuation">[</span>t<span class="token punctuation">]</span><span class="token punctuation">.</span>href<span class="token operator">===</span>n<span class="token punctuation">)</span><span class="token keyword">return</span> <span class="token function">e</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token function">setTimeout</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token function">u</span><span class="token punctuation">(</span>e<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><span class="token keyword">return</span> l<span class="token punctuation">.</span>addEventListener<span class="token operator">&amp;&amp;</span>l<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"load"</span><span class="token punctuation">,</span>r<span class="token punctuation">)</span><span class="token punctuation">,</span>l<span class="token punctuation">.</span>onloadcssdefined<span class="token operator">=</span>u<span class="token punctuation">,</span><span class="token function">u</span><span class="token punctuation">(</span>r<span class="token punctuation">)</span><span class="token punctuation">,</span>l<span class="token punctuation">}</span><span class="token punctuation">;</span><span class="token string">"undefined"</span><span class="token operator">!=</span><span class="token keyword">typeof</span> exports<span class="token operator">?</span>exports<span class="token punctuation">.</span>loadCSS<span class="token operator">=</span>n<span class="token operator">:</span>e<span class="token punctuation">.</span>loadCSS<span class="token operator">=</span>n<span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token string">"undefined"</span><span class="token operator">!=</span><span class="token keyword">typeof</span> global<span class="token operator">?</span>global<span class="token operator">:</span><span class="token keyword">this</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">&lt;/</span>script</span><span class="token punctuation">></span></span><br><br> <span class="token comment">&lt;!-- Critical CSS --></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</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>inlineCSS<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">.site__nav ul</span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span>flex<span class="token punctuation">;</span><span class="token property">flex-wrap</span><span class="token punctuation">:</span>wrap<span class="token punctuation">;</span> <span class="token punctuation">}</span><br> <span class="token comment">/* More critical CSS */</span><br> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span><br><br> <span class="token comment">&lt;!-- If JavaScript is not enabled, just include the full CSS file like normal --></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>noscript</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>stylesheet<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>/assets/css/main.css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>noscript</span><span class="token punctuation">></span></span><br><br> <span class="token comment">&lt;!-- Otherwise, call loadCSS function --></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</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>loadCSS<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><span class="token function">loadCSS</span><span class="token punctuation">(</span><span class="token string">"/assets/css/main.css"</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span></code></pre> <p>Unfortunately, since I’m hosting with Ghost, I don’t have complete control over what is in my document <code>&lt;head&gt;</code>. So, because Ghost inserts some more externally sourced CSS files in my document <code>&lt;head&gt;</code>, my PageSpeed Insights score isn’t as great as it could be.</p> <p><em>shrugs</em></p> <h2 id="what-s-next" tabindex="-1">What's Next? <a class="header-anchor" href="https://bitsofco.de/redesigning-bitsofcode-3/">#</a></h2> <p>Although this new design is an improvement on the previous one in a number of ways, there is definitely still room for more improvement. As I learn more, I will like to make incremental changes as I go.</p> <p>I'd also appreciate any feedback, comments, or suggests I can get. I could only do so much testing myself, so if you are experiencing any issues on the site, do leave a comment.</p> <p><em>✌🏾</em></p> Making Alix, a Chrome Extension for Linting HTML 2017-03-14T00:00:00Z https://bitsofco.de/making-alix-a-chrome-extension-for-linting-html/ <p>Last week, I wrote about how you can <a href="https://bitsofco.de/linting-html-using-css">use CSS selectors to help lint your HTML</a>. The general idea behind this concept was that we can use some of the more advanced CSS selectors, such as <code>:not()</code>, to select certain types of elements in the document. For example, we can select all images that don't have alternative text, and apply styling to highlight them on the page.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">img:not([alt])</span> <span class="token punctuation">{</span><br> <span class="token property">border</span><span class="token punctuation">:</span> 5px solid red<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token comment">/* Add an error message */</span><br><span class="token selector">img:not([alt])::after</span> <span class="token punctuation">{</span><br> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"Images must have an alt attribute"</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Since writing that article, I discovered that quite a <a href="https://heydonworks.com/revenge_css_bookmarklet/">few</a> <a href="https://disq.us/url?url=http%3A%2F%2Farchive.oreilly.com%2Fpub%2Fa%2Fnetwork%2F2000%2F07%2F21%2Fmagazine%2Fcss_tool.html%3Aq4ix0_OUH1ABT47phcu1age-5Tw&amp;cuid=3490249">other</a> <a href="https://disq.us/url?url=https%3A%2F%2Fmathiasbynens.be%2Fnotes%2Fcss-hidden-elements%3AicJwuKHh3fzutZrF7QzsaC0z6Zg&amp;cuid=3490249">people</a> have used this idea before. However, the most extensive one I have found was <a href="https://ffoodd.github.io/a11y.css/">a11y.css</a>, created by <a href="https://twitter.com/ffoodd_fr">Gaël Poupard</a>. So, instead of creating my own, I've decided to just help make that one better.</p> <p>To make this much easier to use for anyone, I decided to make it into a browser extension (in this case, Chrome).</p> <video autoplay="" loop="" muted="" playsinline="" poster="https://res.cloudinary.com/ireaderinokun/image/upload/v1488961489/demo-chrome_cuswu1.jpg" width="780"> <source type="video/webm" src="https://res.cloudinary.com/ireaderinokun/image/upload/v1488961489/demo-chrome_cuswu1.webm"> <img src="https://res.cloudinary.com/ireaderinokun/image/upload/v1488961489/demo-chrome_cuswu1.jpg"> </video> <p><a href="https://chrome.google.com/webstore/detail/alix-for-chrome/aepmadgjacfjcneccddiccnkbpimobge">Get the Extension</a> | <a href="https://github.com/ireade/alix">View Source</a></p> <p>The extension allows you easily apply the stylesheet to any page. Here's how I made it.</p> <h2 id="declaring-a-chrome-extension-the-manifest" tabindex="-1">Declaring a Chrome Extension: The Manifest <a class="header-anchor" href="https://bitsofco.de/making-alix-a-chrome-extension-for-linting-html/">#</a></h2> <p>The first thing we need to do to create an extension for Chrome is to create a Manifest file. This works in a similar way to the manifest file we create for Progressive Web Apps. It includes various bits of information that Chrome needs to determine what the extension does, and what permissions it requires.</p> <p>This is the <code>manifest.json</code> file for Alix -</p> <pre class="language-json" tabindex="0"><code class="language-json"><span class="token punctuation">{</span><br> <span class="token property">"manifest_version"</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span><br><br> <span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"Alix for Chrome"</span><span class="token punctuation">,</span><br> <span class="token property">"short_name"</span><span class="token operator">:</span> <span class="token string">"Alix"</span><span class="token punctuation">,</span><br> <span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"Lorem ipsum"</span><span class="token punctuation">,</span><br> <span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"1.1"</span><span class="token punctuation">,</span><br><br> <span class="token property">"permissions"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token string">"activeTab"</span><br> <span class="token punctuation">]</span><span class="token punctuation">,</span><br><br> <span class="token property">"browser_action"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token property">"default_title"</span><span class="token operator">:</span> <span class="token string">"Toggle Alix"</span><span class="token punctuation">,</span><br> <span class="token property">"default_popup"</span><span class="token operator">:</span> <span class="token string">"popup/index.html"</span><span class="token punctuation">,</span><br> <span class="token property">"default_icon"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token property">"19"</span><span class="token operator">:</span> <span class="token string">"images/toolbar-chrome.png"</span><span class="token punctuation">,</span><br> <span class="token property">"38"</span><span class="token operator">:</span> <span class="token string">"images/toolbar-chrome@2x.png"</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br><br> <span class="token property">"icons"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token property">"128"</span><span class="token operator">:</span> <span class="token string">"icon_128.png"</span><span class="token punctuation">,</span><br> <span class="token property">"16"</span><span class="token operator">:</span> <span class="token string">"icon_16.png"</span><span class="token punctuation">,</span><br> <span class="token property">"48"</span><span class="token operator">:</span> <span class="token string">"icon_48.png"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br><br> <span class="token property">"web_accessible_resources"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token string">"a11y.css/a11y-en_advice.css"</span><span class="token punctuation">,</span><br> <span class="token string">"a11y.css/a11y-en_error.css"</span><span class="token punctuation">,</span><br> <span class="token string">"a11y.css/a11y-en_obsolete.css"</span><span class="token punctuation">,</span><br> <span class="token string">"a11y.css/a11y-en_warning.css"</span><span class="token punctuation">,</span><br> <span class="token string">"a11y.css/a11y-fr_advice.css"</span><span class="token punctuation">,</span><br> <span class="token string">"a11y.css/a11y-fr_error.css"</span><span class="token punctuation">,</span><br> <span class="token string">"a11y.css/a11y-fr_obsolete.css"</span><span class="token punctuation">,</span><br> <span class="token string">"a11y.css/a11y-fr_warning.css"</span><br> <span class="token punctuation">]</span><br><span class="token punctuation">}</span></code></pre> <p>Here's what some of the options mean -</p> <ul> <li> <p><code>manifest_version</code>: The version of the manifest file format that the extension requires. As of Chrome 18, version 2 is required.</p> </li> <li> <p><code>permissions</code>: The permissions your extension is requesting access to. Alix only requires access to whichever tab is currently active at the time.</p> </li> <li> <p><code>browser_action</code>: This puts an icon in the main Chrome toolbar, and allows us to specify an action to be taken when that icon is clicked.</p> <ul> <li><code>default_title</code>: The default title for the toolbar icon.</li> <li><code>default_popup</code>: The HTML page to load when the icon is clicked.</li> </ul> </li> <li> <p><code>web_accessible_resources</code>: An array of paths to resources that may be used on the web page.</p> </li> </ul> <h2 id="creating-the-popup" tabindex="-1">Creating the Popup <a class="header-anchor" href="https://bitsofco.de/making-alix-a-chrome-extension-for-linting-html/">#</a></h2> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/dc_SOoCeG0-900.avif 900w"><source type="image/webp" srcset="https://bitsofco.de/img/dc_SOoCeG0-900.webp 900w"><img alt="Popup" loading="lazy" decoding="async" src="https://bitsofco.de/img/dc_SOoCeG0-900.png" width="900" height="530"></picture></p> <p>The popup is just a simple HTML page, which we linked to in our manifest file under <code>browser_action/default_popup</code>. By defining the popup here, it is automatically triggered when the toolbar icon is clicked.</p> <p>We can style this page any way we want like any regular HTML page.</p> <h2 id="applying-the-a11y-css-stylesheet" tabindex="-1">Applying the a11y.css Stylesheet <a class="header-anchor" href="https://bitsofco.de/making-alix-a-chrome-extension-for-linting-html/">#</a></h2> <p>Finally, what we want to do is apply the relevant <code>a11y.css</code> stylesheet to the current active tab. To do that, we have to run a script on the tab which will create a <code>&lt;link rel=&quot;stylsheet&quot;&gt;</code> element on the page, linking to the <code>a11y.css</code> stylesheet.</p> <p>To run a script on the current active tab, we can use the <code>chrome.tabs.executeScript()</code> method. This method accepts an object with some options. The relevant option for this purpose is <code>code</code>, to which we pass a string of the code we want to be executed.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">addStylesheet</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Get file path based on language and level options from form</span><br> <span class="token keyword">const</span> file <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">/a11y.css/a11y-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>options<span class="token punctuation">.</span>language<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">_</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>options<span class="token punctuation">.</span>level<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">.css</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br><br> <span class="token keyword">const</span> code <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string"><br> var stylesheet = document.createElement("link");<br> stylesheet.rel = "stylesheet";<br> stylesheet.href = chrome.extension.getURL("</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">");<br> stylesheet.id = "a11yCSS";<br> document.getElementsByTagName("head")[0].appendChild(stylesheet);<br> </span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br><br> <span class="token comment">// Execute script on active tab</span><br> chrome<span class="token punctuation">.</span>tabs<span class="token punctuation">.</span><span class="token function">executeScript</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">code</span><span class="token operator">:</span> code<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>If we inspect the page, we can see that the stylesheet has been added as the last element in the document head.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/4j0GIwXjNf-1100.avif 1100w"><source type="image/webp" srcset="https://bitsofco.de/img/4j0GIwXjNf-1100.webp 1100w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/4j0GIwXjNf-1100.png" width="1100" height="504"></picture></p> <p>That's it! As always, you can view the <a href="https://github.com/ireade/alix">source code</a> or install the extension from <a href="https://chrome.google.com/webstore/detail/alix-for-chrome/aepmadgjacfjcneccddiccnkbpimobge">Chrome Web Store</a>.</p> <p>Thanks to <a href="https://twitter.com/ffoodd_fr">Gaël Poupard</a> for creating <a href="https://ffoodd.github.io/a11y.css/">a11y.css</a>!</p> Linting HTML using CSS 2017-03-07T00:00:00Z https://bitsofco.de/linting-html-using-css/ <p>When HTML is written incorrectly, nothing much happens. Because of this, it's easy to have invalid, unsemantic, or unaccessible bits in markup without it being obvious.</p> <p>There are many ways we can lint our HTML to discover and fix these issues, for example using the <a href="https://validator.w3.org/">W3C Markup Validation Service</a>. Another thing we can do, which can be more easily integrated into a development workflow, is to use some slightly advanced CSS selectors to highlight potential problem areas. Here are a few things we can use CSS selectors to help us catch out.</p> <h2 id="inline-styles" tabindex="-1">Inline Styles <a class="header-anchor" href="https://bitsofco.de/linting-html-using-css/">#</a></h2> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">*[style]</span> <span class="token punctuation">{</span><br> <span class="token property">border</span><span class="token punctuation">:</span> 5px solid red<span class="token punctuation">;</span> <span class="token comment">/* Style to make the elements noticeable */</span><br><span class="token punctuation">}</span></code></pre> <p>This selector will target any element on the page that has inline styles applied to it. As a general rule, inline styles should be avoided as they are difficult to override due to their increased level of specificity. Although inline styles may be necessary in some cases, this selector will help highlight them so a decision can be made on a case-by-case basis.</p> <p>With the problem elements selected, we can apply any style to make them more visibly obvious on the page, e.g. a big red border.</p> <h2 id="faulty-or-missing-link-targets" tabindex="-1">Faulty or Missing Link Targets <a class="header-anchor" href="https://bitsofco.de/linting-html-using-css/">#</a></h2> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">a:not([href]),<br>a[href="#"],<br>a[href=""],<br>a[href*="javascript:void(0)"]</span> <span class="token punctuation">{</span> … <span class="token punctuation">}</span></code></pre> <p>These selectors will highlight any anchor elements that either do not have any <code>href</code> attribute at all, or have a meaningless one.</p> <h2 id="unaccessible-images" tabindex="-1">Unaccessible Images <a class="header-anchor" href="https://bitsofco.de/linting-html-using-css/">#</a></h2> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">img:not([alt])</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span></code></pre> <p>As a blanket rule, <a href="https://bitsofco.de/alternative-text-and-images">all images should have an <code>alt</code> attribute</a>. When this attribute is missing, most screenreaders will read out the value of the <code>src</code> attribute instead which, of course, is not useful to the user and can in fact be confusing.</p> <p>It should be noted that the above selector will not select images with a null/empty <code>alt</code> attribute, i.e. images with <code>alt=&quot;&quot;</code>. This is because a null <code>alt</code> attribute can be an intentional way of having a screen reader skip over the image, which is useful if, for example, the image is purely decorative. It could, however, still be useful to have these highlighted, which we can do with the following selector -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">img[alt=""]</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span></code></pre> <h2 id="missing-document-language" tabindex="-1">Missing Document Language <a class="header-anchor" href="https://bitsofco.de/linting-html-using-css/">#</a></h2> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">html:not([lang]),<br>html[lang=""]</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span></code></pre> <p>An important attribute that should be present on all <code>html</code> elements is the language attribute. This attribute is a signal to screen readers what language the page is in, which can determine how the content of the page is read aloud.</p> <p>Here's an example of what can happen when the <code>lang</code> attribute is missing -</p> <blockquote> <p>Share <a href="https://twitter.com/HTeuMeuLeu">@HTeuMeuLeu</a>'s video to show why setting a default language (eg. lang=&quot;en&quot;) is important. 😂 <a href="https://t.co/tjn8GvPVKM">https://t.co/tjn8GvPVKM</a> — overflow: heydon (@heydonworks) <a href="https://twitter.com/heydonworks/status/834714116715663361">February 23, 2017</a></p> </blockquote> <h2 id="incorrect-character-set" tabindex="-1">Incorrect Character Set <a class="header-anchor" href="https://bitsofco.de/linting-html-using-css/">#</a></h2> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">meta[charset]:not([charset="UTF-8"])</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span></code></pre> <p>This selector targets any meta character set tag that is not set to <code>UTF-8</code>. This tag tells the browser to use the UTF-8 form of character encoding, which is presently the recommended form for HTML documents. Having this tag is therefore <a href="https://validator.w3.org/docs/help.html#faq-charset">required for valid HTML</a>.</p> <p>Ideally, this tag should also be the first element after the opening <code>&lt;head&gt;</code> tag. We can check for this using the following selector -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">meta[charset="UTF-8"]:not(:first-child)</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span></code></pre> <h2 id="unaccessible-viewport-attributes" tabindex="-1">Unaccessible Viewport Attributes <a class="header-anchor" href="https://bitsofco.de/linting-html-using-css/">#</a></h2> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">meta[name="viewport"][content*="user-scalable=no"],<br>meta[name="viewport"][content*="maximum-scale"],<br>meta[name="viewport"][content*="minimum-scale"]</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span></code></pre> <p>This selector can be used to highlight unaccessible viewport meta attributes. It is generally advised that we avoid restricting the user's ability to manipulate the viewport by shrinking and enlarging it. So, using <code>user-scalable=no</code>, <code>maximum-scale</code>, or <code>minimum-scale</code> should never be used.</p> <h2 id="unlabelled-form-elements" tabindex="-1">Unlabelled Form Elements <a class="header-anchor" href="https://bitsofco.de/linting-html-using-css/">#</a></h2> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">input:not([id]),<br>select:not([id]),<br>textarea:not([id])</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span><br><br><span class="token selector">label:not([for])</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span></code></pre> <p>Form elements are perhaps the most important elements when it comes to labelling. Although there are <a href="https://bitsofco.de/labelling-form-elements">several ways to label a form element</a>, the most common way is by having an ID on the element that is referenced by a label element. The above selector checks for form elements that do not have an ID, and label elements that are not explicitly linked to a form element using the <code>for</code> attribute.</p> <p>Another type of labelling that is important for form elements is via the <code>name</code> attribute. While the <code>id</code> attribute is used for labelling the element in the context of the HTML document, the <code>name</code> attribute is used to reference the the element when submitted with the form data.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">input:not([name]),<br>select:not([name]),<br>textarea:not([name])</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span></code></pre> <p>Additionally, besides the form elements themselves, it is useful to give the form element itself a name and/or id.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">form:not([name]):not([id])</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span></code></pre> <p>This selector will highlight any form element that is missing both <code>name</code> and <code>id</code> attributes.</p> <h2 id="empty-interactive-elements" tabindex="-1">Empty Interactive Elements <a class="header-anchor" href="https://bitsofco.de/linting-html-using-css/">#</a></h2> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">button:empty,<br>a:empty</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span></code></pre> <p>Interactive elements like links or buttons are typically labelled by their content. Although it is possible to label these elements using other methods, such as an <code>aria-label</code> attribute, having them be empty is likely a sign of something wrong. This selector will highlight any links of buttons that have no HTML content inside them.</p> <h2 id="unnecessary-or-deprecated-attributes" tabindex="-1">Unnecessary or Deprecated Attributes <a class="header-anchor" href="https://bitsofco.de/linting-html-using-css/">#</a></h2> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">script[type="text/javascript"],<br>link[rel="stylesheet"][type="text/css"]</span> <span class="token punctuation">{</span> ... <span class="token punctuation">}</span></code></pre> <p>Finally, we can use CSS selectors to highlight attributes in our HTML that are deprecated or no longer needed.</p> <p><strong>Edit (14/03/2017)</strong>: To apply this concept, I made a <a href="https://chrome.google.com/webstore/detail/alix-for-chrome/aepmadgjacfjcneccddiccnkbpimobge">Chrome Extension</a>. I wrote about it in my followup post, <a href="https://bitsofco.de/making-alix-a-chrome-extension-for-linting-html">Making Alix, a Chrome Extension for Linting HTML</a></p> Asynchronous vs Deferred JavaScript 2017-02-28T00:00:00Z https://bitsofco.de/async-vs-defer/ <p>In my article on <a href="https://bitsofco.de/understanding-the-critical-rendering-path">Understanding the Critical Rendering Path</a>, I wrote about the effect JavaScript files have on the Critical Rendering Path.</p> <blockquote> <p>JavaScript is considered a &quot;parser blocking resource&quot;. This means that the parsing of the HTML document itself is blocked by JavaScript. When the parser reaches a <code>&lt;script&gt;</code> tag, whether that be internal or external, it stops to fetch (if it is external) and run it.</p> </blockquote> <p>This behaviour can be problematic if we are loading several JavaScript files on a page, as this will interfere with the time to first paint even if the document is not actually dependent on those files.</p> <p>Fortunately, the <code>&lt;script&gt;</code> element has two attributes, <code>async</code> and <code>defer</code>, that can give us more control over how and when external files are fetched and executed.</p> <h2 id="normal-execution" tabindex="-1">Normal Execution <a class="header-anchor" href="https://bitsofco.de/async-vs-defer/">#</a></h2> <p>Before looking into the effect of the two attributes, we must first look at what occurs in their absence. By default, as mentioned above, JavaScript files will interrupt the parsing of the HTML document in order for them to be fetched (if not inline) and executed.</p> <p>Take, for example, this script element located somewhere in the middle of 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">&lt;</span>html</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> ...<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</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>script.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> ....<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre> <p>As the document parser goes through the page, this is what occurs -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/0ew3Nmrr5h-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/0ew3Nmrr5h-780.webp 780w"><img alt="Normal JavaScript Execution. HTML Parsing paused for script fetching and execution" loading="lazy" decoding="async" src="https://bitsofco.de/img/0ew3Nmrr5h-780.png" width="780" height="152"></picture></p> <p>The HTML parsing is paused for the script to be fetched and executed, thereby extending the amount of time it takes to get to first paint.</p> <h2 id="the-async-attribute" tabindex="-1">The <code>async</code> Attribute <a class="header-anchor" href="https://bitsofco.de/async-vs-defer/">#</a></h2> <p>The <code>async</code> attribute is used to indicate to the browser that the script file <em>can</em> be executed asynchronously. The HTML parser does not need to pause at the point it reaches the script tag to fetch and execute, the execution can happen whenever the script becomes ready after being fetched in parallel with the document parsing.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">async</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>script.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>This attribute is only available for externally located script files. When an external script has this attribute, the file can be downloaded while the HTML document is still parsing. Once it has been downloaded, the parsing is paused for the script to be executed.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/C8LhUc8Nz8-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/C8LhUc8Nz8-780.webp 780w"><img alt="Asynchronous JavaScript Execution. HTML parsing is paused only for the script execution" loading="lazy" decoding="async" src="https://bitsofco.de/img/C8LhUc8Nz8-780.png" width="780" height="152"></picture></p> <h2 id="the-defer-attribute" tabindex="-1">The <code>defer</code> Attribute <a class="header-anchor" href="https://bitsofco.de/async-vs-defer/">#</a></h2> <p>The <code>defer</code> attribute tells the browser to only execute the script file once the HTML document has been fully parsed.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">defer</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>script.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>Like an asynchronously loaded script, the file can be downloaded while the HTML document is still parsing. However, even if the file is fully downloaded long before the document is finished parsing, the script is not executed until the parsing is complete.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/0C_OJcRySz-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/0C_OJcRySz-780.webp 780w"><img alt="Deferred JavaScript Execution. HTML parsing is never paused. Script execution happens after parsing is complete" loading="lazy" decoding="async" src="https://bitsofco.de/img/0C_OJcRySz-780.png" width="780" height="152"></picture></p> <h2 id="asynchronous-deferred-or-normal-execution" tabindex="-1">Asynchronous, Deferred or Normal Execution? <a class="header-anchor" href="https://bitsofco.de/async-vs-defer/">#</a></h2> <p>So, when should we use asynchronous, deferred, or normal JavaScript execution? As always, it depends on the situation, and there are a few questions to consider.</p> <h3 id="where-is-the-script-element-located" tabindex="-1">Where is the <code>&lt;script&gt;</code> element located? <a class="header-anchor" href="https://bitsofco.de/async-vs-defer/">#</a></h3> <p>Asynchronous and deferred execution of scripts are more important when the <code>&lt;script&gt;</code> element is not located at the very end of the document. HTML documents are parsed in order, from the first opening <code>&lt;html&gt;</code> element to it's close. If an externally sourced JavaScript file is placed right before the closing <code>&lt;/body&gt;</code> element, it becomes much less pertinent to use an <code>async</code> or <code>defer</code> attribute. Since the parser will have finished the vast majority of the document by that point, JavaScript files don't have much parsing left to block.</p> <h3 id="is-the-script-self-contained" tabindex="-1">Is the script self-contained? <a class="header-anchor" href="https://bitsofco.de/async-vs-defer/">#</a></h3> <p>For script files that are not dependent on other files and/or do not have any dependencies themselves, the <code>async</code> attribute is particularly useful. Since we do not care exactly at which point the file is executed, asynchronous loading is the most suitable option.</p> <h3 id="does-the-script-rely-on-a-fully-parsed-dom" tabindex="-1">Does the script rely on a fully parsed DOM? <a class="header-anchor" href="https://bitsofco.de/async-vs-defer/">#</a></h3> <p>In many cases, the script file contains functionality that requires interaction with the DOM. Or, it may have a dependency on another file included on the page. In these cases, the DOM must be fully parsed before the script should be executed. Typically, such a file will be placed at the bottom of the page to ensure everything before it has been parsed. However, in situation where, for whatever reason, the file in question needs to be placed elsewhere, the <code>defer</code> attribute can be used.</p> <h3 id="is-the-script-a-small-dependency" tabindex="-1">Is the script a (small) dependency? <a class="header-anchor" href="https://bitsofco.de/async-vs-defer/">#</a></h3> <p>Finally, if the script is relatively small, and/or is depended on by other files, it may be more useful to have it defined inline. Although having it inline will block the parsing of the HTML document, it should not be a significant interference if it's a small size. Additionally, if it is depended on by other files, the minor blocking may be necessary.</p> <h2 id="support-and-modern-browser-engines" tabindex="-1">Support and Modern Browser Engines <a class="header-anchor" href="https://bitsofco.de/async-vs-defer/">#</a></h2> <p>The support for the <code>async</code> and <code>defer</code> attributes is very widespread -</p> <p class="ciu_embed" data-feature="script-async" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false"> <picture> <source type="image/webp" srcset="https://caniuse.bitsofco.de/image/script-async.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/script-async.png"> <img src="https://caniuse.bitsofco.de/image/script-async.jpg" alt="Data on support for the script-async feature across the major browsers from caniuse.com"> </picture> </p> <p class="ciu_embed" data-feature="script-defer" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false"> <picture> <source type="image/webp" srcset="https://caniuse.bitsofco.de/image/script-defer.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/script-defer.png"> <img src="https://caniuse.bitsofco.de/image/script-defer.jpg" alt="Data on support for the script-defer feature across the major browsers from caniuse.com"> </picture> </p> <p>It is worth noting that the behaviour of these attributes may be slightly different across different JavaScript engines. For example, in V8 (used in Chromium), an attempt is made to parse all scripts, regardless of their attributes, on a separate dedicated thread for script execution. This way, the &quot;parser blocking&quot; nature of JavaScript files should be minimised as a default.</p> "What's the typeof null?", and other confusing JavaScript Types 2017-02-21T00:00:00Z https://bitsofco.de/javascript-typeof/ <p>The <code>typeof</code> operator in JavaScript evaluates and returns a string with the data type of an operand. For example, to find the type of <code>123</code>, we would write -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">typeof</span> <span class="token number">123</span></code></pre> <p>This will return a string with the type of <code>123</code>, which, in this case, will be &quot;number&quot;. In addition to &quot;number&quot;, the <code>typeof</code> operator can return one of 6 potential results -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">typeof</span> <span class="token number">123</span> <span class="token comment">// "number"</span><br><span class="token keyword">typeof</span> <span class="token string">"abc"</span> <span class="token comment">// "string"</span><br><span class="token keyword">typeof</span> <span class="token boolean">true</span> <span class="token comment">// "boolean"</span><br><span class="token keyword">typeof</span> <span class="token punctuation">{</span><span class="token literal-property property">a</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">}</span> <span class="token comment">// "object"</span><br><span class="token keyword">typeof</span> <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// "function"</span><br><span class="token keyword">typeof</span> <span class="token keyword">undefined</span> <span class="token comment">// "undefined"</span><br><span class="token keyword">typeof</span> <span class="token function">Symbol</span><span class="token punctuation">(</span><span class="token string">'foo'</span><span class="token punctuation">)</span> <span class="token comment">// "symbol"</span></code></pre> <p>In the above examples, it's pretty straightforward what the type of the operands will be. However, there are a few cases where it is unclear or misunderstood what the type of the operand should be.</p> <h2 id="what-s-the-typeof-typeof-123" tabindex="-1">What's the <code>typeof typeof 123</code>? <a class="header-anchor" href="https://bitsofco.de/javascript-typeof/">#</a></h2> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">typeof</span> <span class="token keyword">typeof</span> <span class="token number">123</span> <span class="token comment">// "string"</span></code></pre> <p>What is the type of a <code>typeof</code> expression? Well, the <code>typeof</code> operator always returns a string with the type of the operand passed to it. If the resultant type of the expression is, for example, a number, what will be returned is <code>&quot;number&quot;</code>. This means that, regardless of resultant type, the type of a <code>typeof [any operand]</code>, will always be a string.</p> <h2 id="what-s-the-typeof-nan" tabindex="-1">What's the <code>typeof NaN</code>? <a class="header-anchor" href="https://bitsofco.de/javascript-typeof/">#</a></h2> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">typeof</span> <span class="token number">NaN</span> <span class="token comment">// "number"</span></code></pre> <p>The type of <code>NaN</code>, which stands for Not a Number is, surprisingly, a number. The reason for this is, in computing, <code>NaN</code> is actually technically a numeric data type. However, it is a numeric data type whose value cannot be represented using actual numbers. So, the name &quot;Not a Number&quot;, doesn't mean that the value is not numeric. It instead means that the value cannot be expressed with numbers.</p> <p>This also explains why not all <code>NaN</code> values are equal. For example -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> NaN1 <span class="token operator">=</span> <span class="token number">2</span> <span class="token operator">*</span> <span class="token string">"abc"</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> NaN2 <span class="token operator">=</span> <span class="token number">2</span> <span class="token operator">*</span> <span class="token string">"abc"</span><span class="token punctuation">;</span><br><br>NaN1 <span class="token operator">===</span> NaN2 <span class="token comment">// false</span></code></pre> <p>Those two <code>NaN</code> values are not equal because they are not necessarily the same unrepresentable number.</p> <h2 id="what-s-the-typeof-1-2-3" tabindex="-1">What's the <code>typeof [1,2,3]</code>? <a class="header-anchor" href="https://bitsofco.de/javascript-typeof/">#</a></h2> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">typeof</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 number">3</span><span class="token punctuation">]</span> <span class="token comment">// "object"</span></code></pre> <p>The <code>typeof</code> an array is an object. In JavaScript, arrays are technically objects; just with special behaviours and abilities. For example, arrays have a <code>Array.prototype.length</code> property, which will return the number of elements in the array. Arrays also have special methods, e.g. <code>Array.prototype.push()</code> or <code>Array.prototype.unshift()</code> (See <a href="https://bitsofco.de/javascript-array-methods-mutator-methods">JavaScript Array Methods - Mutator Methods</a>).</p> <p>To differentiate an Array object from an Object object, we can use the <code>Array.isArray()</code> method.</p> <pre class="language-js" tabindex="0"><code class="language-js">Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</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 number">3</span><span class="token punctuation">]</span> <span class="token punctuation">)</span> <span class="token comment">// true</span><br>Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span> <span class="token punctuation">{</span> <span class="token literal-property property">a</span><span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token comment">// false</span></code></pre> <h2 id="what-s-the-typeof-null" tabindex="-1">What's the <code>typeof null</code>? <a class="header-anchor" href="https://bitsofco.de/javascript-typeof/">#</a></h2> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">typeof</span> <span class="token keyword">null</span> <span class="token comment">// "object"</span></code></pre> <p>The <code>null</code> value is technically a primitive, the way &quot;object&quot; or &quot;number&quot; are primitives. This would typically mean that the type of null should also be &quot;null&quot;. However, this is not the case because of a peculiarity with the way JavaScript was first defined.</p> <p>In the first implementation of JavaScript, values were represented in two parts - a type tag and the actual value. There were 5 type tags that could be used, and the tag for referencing an object was <code>0</code>. The <code>null</code> value, however, was represented as the <code>NULL</code> pointer, which was <code>0x00</code> for most platforms. As a result of this similarity, null has the <code>0</code> type tag, which corresponds to an object.</p> <h2 id="what-s-the-typeof-class-foo" tabindex="-1">What's the <code>typeof class Foo {}</code>? <a class="header-anchor" href="https://bitsofco.de/javascript-typeof/">#</a></h2> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">typeof</span> <span class="token keyword">class</span> <span class="token class-name">Foo</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token comment">// "function"</span></code></pre> <p>Finally, we have Classes. Classes were introduced in ES2015 (ES6) as a better syntax for prototype-based inheritance. Before Classes, to make an inheritable object, we would have to use a function.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">Dog</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 class-name">Dog</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">bark</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">"woof!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token keyword">const</span> snoopy <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Dog</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>snoopy<span class="token punctuation">.</span><span class="token function">bark</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// alert("woof!")</span></code></pre> <p>With Classes, we can create the same object this way -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">class</span> <span class="token class-name">Dog</span> <span class="token punctuation">{</span><br> <span class="token function">bark</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">"woof!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><br><br><span class="token keyword">const</span> snoopy <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Dog</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>snoopy<span class="token punctuation">.</span><span class="token function">bark</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token comment">// alert("woof!")</span></code></pre> <p>However, Classes have always just been a syntactical wrapper around the function method. The same function is actually being created, but just with the author writing it in a different, cleaner way. This is why the <code>typeof</code> a Class, is still just a Function.</p> Optimising GIFs for the Web 2017-02-14T00:00:00Z https://bitsofco.de/optimising-gifs/ <p>Like a lot of people, I like GIFs. I like to use them in my articles to illustrate functionality. For example, this GIF from my article on <a href="https://bitsofco.de/challenge-itunes-library">&quot;Recreating the iTunes Library&quot;</a> -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/LeikwKYEkO-770.avif 770w"><source type="image/gif" srcset="https://bitsofco.de/img/LeikwKYEkO-770.gif 770w"><img alt="Example GIF Original File" loading="lazy" decoding="async" src="https://bitsofco.de/img/LeikwKYEkO-770.webp" width="770" height="286"></picture></p> <p>However, a problem with this is that GIFs are <em>heavy</em>, the one above is a whopping 11.4 MB 😱 (NB: not exactly the image above, I couldn't <em>actually</em> load that on a page). Recently, I’ve found that some of my articles that are GIF-heavy tend to get incredibly slow. The reason for this is that each frame in a GIF is stored as a GIF image, which uses a lossless compression algorithm. This means that, during compression, no information is lost in the image at all, which of course results in a larger file size.</p> <p>To solve the performance problem of GIFs on the web, there are a couple of things we can do.</p> <h2 id="use-html5-video" tabindex="-1">Use HTML5 Video <a class="header-anchor" href="https://bitsofco.de/optimising-gifs/">#</a></h2> <p>Surprisingly, the lossless compression algorithm used on GIFs is so unoptimised that video formats such as MP4 or WebM will provide a smaller file size than GIF images. Because of this, one solution to the GIF performance problem is to not use GIFs at all, and to replace them with autoplaying, looping, HTML5 Video.</p> <p>By applying certain attributes to the <code>&lt;video&gt;</code> element, we can simulate the behaviour of a GIF, but with a much smaller file size. The attributes we need are -</p> <ul> <li><code>autoplay</code>: Immediately start playing the video without the user needing to press &quot;play&quot;</li> <li><code>loop</code>: Loop the video infinitely</li> <li><code>muted</code>: Although there is no audio track on the GIF, stating this attribute is needed for iOS Safari to autoplay the video</li> <li><code>playsinline</code> : For iOS Safari, to make sure that the video is not moved to fullscreen mode</li> <li><code>poster</code>: Specifies an image to be displayed while the video is downloading</li> </ul> <p>To replace the GIF from the introduction to this article, we can use the following video element -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>video</span> <span class="token attr-name">autoplay</span> <span class="token attr-name">loop</span> <span class="token attr-name">muted</span> <span class="token attr-name">playsinline</span> <span class="token attr-name">poster</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>original.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">&lt;</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>video/webm<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>original.webm<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">&lt;</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>original.gif<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">&lt;/</span>video</span><span class="token punctuation">></span></span></code></pre> <p>This will give us a video that's much smaller in size, at only 1MB 😱😱!</p> <video autoplay="" loop="" muted="" playsinline="" poster="https://res.cloudinary.com/ireaderinokun/image/upload/v1485711823/lossy-compressed_s9cypz.jpg"> <source type="video/webm" src="https://res.cloudinary.com/ireaderinokun/image/upload/v1485711823/lossy-compressed_s9cypz.webm"> <img src="https://bitsofco.de/content/images/2017/01/lossy-compressed.gif"> </video> <p>To convert a GIF to WebM, we can use <a href="https://cloudconvert.com/gif-to-webm">CloudConvert</a>. Or, if you use <a href="https://cloudinary.com">Cloudinary</a>, you can just change the file extension from .gif to .webm to get the video format.</p> <h2 id="lossy-optimisation" tabindex="-1">Lossy Optimisation <a class="header-anchor" href="https://bitsofco.de/optimising-gifs/">#</a></h2> <p>In some cases, because HTML5 Video doesn't work everywhere, we can't get around having to use an actual GIF. For example, when this blog post is delivered as an HTML email, an actual GIF has to be used. So, there are some optimisations we can make to the GIF itself to make it more performant.</p> <p>As I mentioned, GIF compression algorithm is lossless. However, there are options for <em>lossy</em> compressions as well. Although this may sound like we will get visibly lower quality GIFs, lossy compression done well should not noticeably degrade the quality of the image.</p> <p>There are a number of tools we can use for lossy GIF compression. One popular tool for optimising GIFs is <a href="https://github.com/kohler/gifsicle">gifsicle</a> and <a href="https://github.com/pornel/giflossy">giflossy</a>. Gifsicle is a CLI for manipulating GIF image files, and giflossy is a fork of gifsicle which offers a lossy compression option (<code>--lossy</code>).</p> <p>To use giflossy to apply a lossy compression to a GIF image, we can use the following command -</p> <pre><code>gifsicle -O3 --lossy=80 -o compressed.gif original.gif </code></pre> <p>The <code>-03</code> option tells gifsicle to attempt several methods for optimisation to find the most suitable. The <code>--lossy=80</code> option specifies how much to apply lossy compression. Depending on your needs, you can adjust this number. The <code>-o compressed.gif</code> option specifies what the output GIF should be called. Finally, we define the path to the original GIF.</p> <p>Using this on the GIF example from before, we go from a 11.4MB GIF to a 6MB GIF, a 47% reduction in the size!</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/LeikwKYEkO-770.avif 770w"><source type="image/gif" srcset="https://bitsofco.de/img/LeikwKYEkO-770.gif 770w"><img alt="Compressed GIF" loading="lazy" decoding="async" src="https://bitsofco.de/img/LeikwKYEkO-770.webp" width="770" height="286"></picture></p> <p>With these two methods combined, we can make use of GIFs in a way that doesn't degrade performance so drastically.</p> CSS Grid Layout Terminology, Explained 2017-02-07T00:00:00Z https://bitsofco.de/css-grid-terminology/ <p>In the <a href="https://www.w3.org/TR/css-grid-1/">CSS Grid Layout Specification</a>, a grid is defined as the following -</p> <blockquote> <p>The grid is an intersecting set of horizontal and vertical grid lines that divides the grid container’s space into grid areas, into which grid items (representing the grid container’s content) can be placed</p> </blockquote> <p>Err… what? 🤔</p> <p>CSS Grid Layout introduces a lot of new concepts; there are 17 new properties to learn, and many more new terms to understand. This can make getting started with CSS Grid Layout difficult, as new terms reference other terms and you can get into a spiral of confusion. So, here are the basic concepts and terminology of CSS Grid Layout, explained.</p> <h2 id="the-grid-container-and-grid-items" tabindex="-1">The Grid Container and Grid Items <a class="header-anchor" href="https://bitsofco.de/css-grid-terminology/">#</a></h2> <p>A Grid starts with the <strong>grid container</strong>. This, as it's name suggests, is the element that contains the elements of the grid. A grid container is created by setting the <code>display</code> property to any of the following values:</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.grid-container</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span> <span class="token comment">/* Will create a block-level grid container */</span><br> <span class="token property">display</span><span class="token punctuation">:</span> inline-grid<span class="token punctuation">;</span> <span class="token comment">/* Will create an inline-level grid container */</span><br> <span class="token property">display</span><span class="token punctuation">:</span> subgrid<span class="token punctuation">;</span> <span class="token comment">/* Used on grid items that are also grid containers */</span><br><span class="token punctuation">}</span></code></pre> <p>The grid container can be thought of like the flex container in the <a href="https://www.w3.org/TR/css-flexbox-1/">Flexible Box Layout Module</a> (created with <code>display: flex</code> or <code>display: inline-flex</code>), or a basic Table (created with <code>display: table</code> or <code>display: inline-table</code>).</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/osLyADYggF-500.avif 500w"><source type="image/webp" srcset="https://bitsofco.de/img/osLyADYggF-500.webp 500w"><img alt="Grid Container" loading="lazy" decoding="async" src="https://bitsofco.de/img/osLyADYggF-500.png" width="500" height="260"></picture></p> <p>A grid container establishes a new <strong>grid formatting context</strong> for its child elements, which are now <strong>grid items</strong>. Special rules apply to grid items, for example -</p> <ul> <li>The <code>float</code> and <code>clear</code> properties do not apply to grid items</li> <li>The <code>vertical-align</code> property does not affect grid items</li> <li>The <code>::first-line</code> and <code>::first-letter</code> pseudo-elements do not apply to the grid container</li> </ul> <h2 id="grid-lines" tabindex="-1">Grid Lines <a class="header-anchor" href="https://bitsofco.de/css-grid-terminology/">#</a></h2> <p><strong>Grid lines</strong> are the horizontal and vertical lines which divide the grid. Each grid line has a referencing number, starting from the outer-most border of the grid container.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/ilKyIWgteQ-594.avif 594w"><source type="image/webp" srcset="https://bitsofco.de/img/ilKyIWgteQ-594.webp 594w"><img alt="Grid Container Showing Horizontal and Vertical Grid Lines" loading="lazy" decoding="async" src="https://bitsofco.de/img/ilKyIWgteQ-594.png" width="594" height="333"></picture></p> <p>The grid line number is used when placing grid items within the container. We can specify that a grid item be placed at a specific grid line, or span one grid line to another.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.grid-container</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 punctuation">:</span> 100px 100px 100px<span class="token punctuation">;</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 100px 100px 100px<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><span class="token selector">.grid-item</span> <span class="token punctuation">{</span><br> <span class="token property">grid-column</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span><br> <span class="token property">grid-row-start</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span><br> <span class="token property">grid-row-end</span><span class="token punctuation">:</span> 3<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/k5rFxhhRWZ-594.avif 594w"><source type="image/webp" srcset="https://bitsofco.de/img/k5rFxhhRWZ-594.webp 594w"><img alt="Grid Item spanning rows 1 to 3 in column 2" loading="lazy" decoding="async" src="https://bitsofco.de/img/k5rFxhhRWZ-594.png" width="594" height="333"></picture></p> <p>Grid lines can also be referenced by an author-given name. The example below will achieve the same result, but using named grid lines.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.grid-container</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 punctuation">:</span> 100px [grid-item-start] 100px [grid-item-end] 100px<span class="token punctuation">;</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> [grid-item-start] 100px 100px [grid-item-end] 100px<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><span class="token selector">.grid-item</span> <span class="token punctuation">{</span><br> <span class="token property">grid-column</span><span class="token punctuation">:</span> grid-item-start / grid-item-end<span class="token punctuation">;</span><br> <span class="token property">grid-row-start</span><span class="token punctuation">:</span> grid-item-start<span class="token punctuation">;</span><br> <span class="token property">grid-row-end</span><span class="token punctuation">:</span> grid-item-end<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="grid-columns-grid-rows-and-grid-tracks" tabindex="-1">Grid Columns, Grid Rows, and Grid Tracks <a class="header-anchor" href="https://bitsofco.de/css-grid-terminology/">#</a></h2> <p>A <strong>grid column</strong> is the space between two adjacent vertical grid lines. The size of a grid column is determined by the <code>grid-template-columns</code> property. On the other hand, a <strong>grid row</strong> is the space between two adjacent horizontal grid lines. It's size is determined by the <code>grid-template-rows</code> property.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.grid-container</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 punctuation">:</span> 65px 1fr 65px<span class="token punctuation">;</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 100px 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/wVvOOZDVa6-417.avif 417w"><source type="image/webp" srcset="https://bitsofco.de/img/wVvOOZDVa6-417.webp 417w"><img alt="Sizing Grid Columns and Grid Rows" loading="lazy" decoding="async" src="https://bitsofco.de/img/wVvOOZDVa6-417.png" width="417" height="277"></picture></p> <p>A <strong>grid track</strong> is a just generic term for either a grid column or a grid row.</p> <h2 id="grid-cells" tabindex="-1">Grid Cells <a class="header-anchor" href="https://bitsofco.de/css-grid-terminology/">#</a></h2> <p>A <strong>grid cell</strong> is the intersection between a grid column and a grid row. It is a space bound by exactly four grid lines, which makes it the smallest available space within the grid.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/FE7KQNIE4i-475.avif 475w"><source type="image/webp" srcset="https://bitsofco.de/img/FE7KQNIE4i-475.webp 475w"><img alt="Grid Cells" loading="lazy" decoding="async" src="https://bitsofco.de/img/FE7KQNIE4i-475.png" width="475" height="239"></picture></p> <h2 id="grid-areas" tabindex="-1">Grid Areas <a class="header-anchor" href="https://bitsofco.de/css-grid-terminology/">#</a></h2> <p>A <strong>Grid area</strong> is any area of space within the grid container that is bound by four grid lines. It must consist of at least one, but potentially more, adjacent grid cells. This means that all grid cells are also grid areas.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Ms1A1NYZWq-247.avif 247w"><source type="image/webp" srcset="https://bitsofco.de/img/Ms1A1NYZWq-247.webp 247w"><img alt="Grid Areas" loading="lazy" decoding="async" src="https://bitsofco.de/img/Ms1A1NYZWq-247.png" width="247" height="234"></picture></p> <p>We can make an element a grid area by using the <code>grid-area</code> property, and assign that area to a space in the grid using the <code>grid-template-areas</code> property (or longhand <code>grid-template-columns</code> or <code>grid-template-rows</code> properties).</p> <p>For example to assign the element, <code>.outlined</code>, to the space outlined in purple above, we can do this -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.outlined</span> <span class="token punctuation">{</span><br> <span class="token property">grid-area</span><span class="token punctuation">:</span> outlined<span class="token punctuation">;</span><br> <span class="token property">border</span><span class="token punctuation">:</span> 2px solid purple<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">.grid-container</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-areas</span><span class="token punctuation">:</span> <span class="token string">"outlined outlined ."</span><br> <span class="token string">"outlined outlined ."</span><br> <span class="token string">". . ."</span><span class="token punctuation">;</span><br> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> 100px 100px 100px<span class="token punctuation">;</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 100px 100px 100px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="the-grid" tabindex="-1">The Grid <a class="header-anchor" href="https://bitsofco.de/css-grid-terminology/">#</a></h2> <p>The grid itself is the sum of all these parts. Revisiting the definition from the start,</p> <blockquote> <p>The grid is an intersecting set of horizontal and vertical grid lines that divides the grid container’s space into grid areas, into which grid items (representing the grid container’s content) can be placed</p> </blockquote> <p>Hopefully, this definition is a little more clear!</p> How calc() Works 2017-01-31T00:00:00Z https://bitsofco.de/how-calc-works/ <p>The CSS3 <code>calc()</code> function allows us to perform mathematical operations on property values. Instead of declaring, for example, static pixel values for an element's width, we can use <code>calc()</code> to specify that the width be the result of the addition of two or more numeric values.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100px + 50px<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="why-calc" tabindex="-1">Why <code>calc()</code>? <a class="header-anchor" href="https://bitsofco.de/how-calc-works/">#</a></h2> <p>If you have used CSS pre-processors like SASS, the above example is something you may have come across.</p> <pre class="language-scss" tabindex="0"><code class="language-scss"><span class="token selector">.foo </span><span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 100px <span class="token operator">+</span> 50px<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token comment">// Or using SASS variables</span><br><span class="token property"><span class="token variable">$width-one</span></span><span class="token punctuation">:</span> 100px<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$width-two</span></span><span class="token punctuation">:</span> 50px<span class="token punctuation">;</span><br><span class="token selector">.bar </span><span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token variable">$width-one</span> <span class="token operator">+</span> <span class="token variable">$width-two</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>However, the <code>calc()</code> function provides a better solution for two reasons. First, <strong>we can combine different units</strong>. Specifically, we can mix relative units such as percentages and viewport units, with absolute units such as pixels. For example, we can create an expression that will subtract a pixel value from a percentage value.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 50px<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>In this example, the <code>.foo</code> element will always have a width that is 50px less than 100% of it's parent width.</p> <p>Second, with <code>calc()</code>, <strong>the computed value is the expression itself</strong>, not the resulting value of the expression. When doing mathematical expressions with CSS pre-processors, the value given to the browser is the resulting value of the expression.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">// Value specified in SCSS<br>.foo</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 100px + 50px<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">// Compiled CSS and computed value in browser<br>.foo</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 150px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>However, with <code>calc()</code> the value parsed by the browser is the actual <code>calc()</code> expression.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">// Value specified in CSS<br>.foo</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 50px<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">// Computed value in browser<br>.foo</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 50px<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>What this means is that the values in the browser can be more dynamic, and adapt as the viewport changes. We can have an element with a height of the viewport minus an absolute value, and it will adapt as the viewport changes.</p> <h2 id="using-calc" tabindex="-1">Using <code>calc()</code> <a class="header-anchor" href="https://bitsofco.de/how-calc-works/">#</a></h2> <p>The <code>calc()</code> function can be used to perform addition, subtraction, multiplication, and division calculations with numeric property values. Specifically, it can be used with <code>&lt;length&gt;</code>, <code>&lt;frequency&gt;</code>, <code>&lt;angle&gt;</code>, <code>&lt;time&gt;</code>, <code>&lt;number&gt;</code>, or <code>&lt;integer&gt;</code> <a href="https://bitsofco.de/generic-css-data-types">data types</a>.</p> <p>Here are a few examples -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>50vmax + 3rem<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">padding</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>1vw + 1em<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span> <span class="token function">calc</span><span class="token punctuation">(</span>1turn + 28deg<span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span>100<span class="token punctuation">,</span> <span class="token function">calc</span><span class="token punctuation">(</span>3 * 20%<span class="token punctuation">)</span><span class="token punctuation">,</span> 40%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>50vw / 3<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h3 id="nesting-calc-s" tabindex="-1">Nesting <code>calc()</code>s <a class="header-anchor" href="https://bitsofco.de/how-calc-works/">#</a></h3> <p><code>calc()</code> functions can be nested. Inner functions, however, will be treated as simple parenthesised expressions. Take, for example, the following nested expression -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span> 100% / <span class="token function">calc</span><span class="token punctuation">(</span>100px * 2<span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>The computed value of this function will be as follows -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span> 100% / <span class="token punctuation">(</span>100px * 2<span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h3 id="providing-a-fallback" tabindex="-1">Providing a Fallback <a class="header-anchor" href="https://bitsofco.de/how-calc-works/">#</a></h3> <p>The support for <code>calc()</code> is relatively widespread.</p> <p class="ciu_embed" data-feature="calc" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false"> <picture> <source type="image/webp" srcset="https://caniuse.bitsofco.de/image/calc.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/calc.png"> <img src="https://caniuse.bitsofco.de/image/calc.jpg" alt="Data on support for the calc feature across the major browsers from caniuse.com"> </picture> </p> <p>For browsers that don't support <code>calc()</code> as a value, the entire property-value expression is ignored. This means that we can easily provide a fallback static value that will be used by non-supporting browsers.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 90%<span class="token punctuation">;</span> <span class="token comment">/* Fallback for older browsers */</span><br> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 50px<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="when-can-we-use-calc" tabindex="-1">When can we use <code>calc()</code>? <a class="header-anchor" href="https://bitsofco.de/how-calc-works/">#</a></h2> <p>The <code>calc()</code> function can be useful in a variety of situations.</p> <h3 id="example-1-centering-elements" tabindex="-1">Example 1 - Centering Elements <a class="header-anchor" href="https://bitsofco.de/how-calc-works/">#</a></h3> <p>Using <code>calc()</code> provides us yet another solution to the age-old problem of centering elements horizontally and vertically within a container. If we know the dimensions of the child element, a typical solution is to use negative margins to shift the element by half it's height and width, like this -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">// Assuming .foo is 300px height and 300px width<br>.foo</span> <span class="token punctuation">{</span><br> <span class="token property">position</span><span class="token punctuation">:</span> absolute<br> <span class="token property">top</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token property">left</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token property">marging-top</span><span class="token punctuation">:</span> -150px<span class="token punctuation">;</span><br> <span class="token property">margin-left</span><span class="token punctuation">:</span> -150px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Using the <code>calc()</code> function, we can achieve all of this using only on the top and left properties.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">position</span><span class="token punctuation">:</span> absolute<br> <span class="token property">top</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>50% - 150px<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">left</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>50% - 150px<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>With the introduction of Flexbox, methods like this are less likely to be needed. However, in cases where Flexbox can't be used, e.g. if the element needs to be positioned absolutely or fixed, this method can be useful.</p> <h3 id="example-2-creating-a-root-grid-size" tabindex="-1">Example 2 - Creating a Root Grid Size <a class="header-anchor" href="https://bitsofco.de/how-calc-works/">#</a></h3> <p>The <code>calc()</code> function can be used to create a viewport-based grid out of the <code>rem</code> unit. We can do this by setting the root element's font-size to be a fraction of the full viewport width.</p> <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> <span class="token function">calc</span><span class="token punctuation">(</span>100vw / 30<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Now, <code>1rem</code> will correlate to 1/30 of the viewport width. For any text on our page, this means that it will be automatically scaled based on the viewport. Further, given the same dimension of viewport, the same amount of text will always be on the screen no matter the actual size of the viewport.</p> <video width="780px" style="width: 100%;" autoplay="" loop="" muted="" playsinline="" poster="https://res.cloudinary.com/ireaderinokun/image/upload/c_scale,q_59,w_780/v1486662717/ezgif.com-optimize_w09wsb.jpg"> <source type="video/webm" src="https://res.cloudinary.com/ireaderinokun/image/upload/f_auto/v1486662717/ezgif.com-optimize_w09wsb.webm"> <img src="https://res.cloudinary.com/ireaderinokun/image/upload/c_scale,q_59,w_780/v1486662717/ezgif.com-optimize_w09wsb.jpg"> </video> <p>If we size other non-textual elements on the page using rem units, they will also follow this behaviour. An element with a width of 1rem will always by 1/30 of the viewport width.</p> <h3 id="example-3-clarity" tabindex="-1">Example 3 - Clarity <a class="header-anchor" href="https://bitsofco.de/how-calc-works/">#</a></h3> <p>Finally, <code>calc()</code> can be useful for making any calculations being done more obvious. For example, if you want a group of items to be 1/6th the width of their parent container, you could write it like this -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 16.666666667%<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>However, it would be much more clear to people reading the CSS to write -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% / 6<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>There are many more things we can do with <code>calc()</code>, such as <a href="https://www.sitepoint.com/creative-grid-system-sass-calc/">creating a Grid System</a>. It's definitely one of the most useful newer features in CSS.</p> Rules for Using ARIA in HTML 2017-01-24T00:00:00Z https://bitsofco.de/rules-for-using-aria-in-html/ <p>The Web Accessibility Initiative's <a href="https://www.w3.org/WAI/intro/aria">Accessible Rich Internet Applications Suite</a> (WAI-ARIA, or just ARIA) is a set of tools and guidelines for making web content and applications more accessible. Most notably, it includes a suite of attributes we can add to HTML elements to embed in them more semantic information that can be read by assistive technologies.</p> <p>Although ARIA can be incredibly useful, we have to be careful of when and how we use it. Thus, there are 5 rules to take into account when using ARIA in HTML.</p> <h2 id="1-use-semantic-html5-in-favour-of-aria" tabindex="-1">1. Use Semantic HTML5 in Favour of ARIA <a class="header-anchor" href="https://bitsofco.de/rules-for-using-aria-in-html/">#</a></h2> <blockquote> <p>If you can use a native HTML element or attribute with the semantics and behaviour you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.</p> </blockquote> <p>The number one rule for using ARIA in HTML, is to try not to use ARIA in HTML (if it’s not needed). HTML5 semantic elements provide us a wide range of elements that come with implicit meaning to them, similar to the explicit meaning we can define using ARIA.</p> <p>So, wherever possible, we should use a semantic HTML element in place of an ARIA attribute.</p> <p>Instead of creating a button using a <code>&lt;div&gt;</code> and ARIA role 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">&lt;</span>div</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>button<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">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <div class="code-addendum warning">Incorrect method</div> <p>We should just use an actual <code>&lt;button&gt;</code> element, 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">&lt;</span>button</span><span class="token punctuation">></span></span>Click Me<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></code></pre> <h2 id="2-don-t-alter-the-meaning-of-semantic-elements-with-aria-roles" tabindex="-1">2. Don’t Alter the Meaning of Semantic Elements with ARIA Roles <a class="header-anchor" href="https://bitsofco.de/rules-for-using-aria-in-html/">#</a></h2> <blockquote> <p>Do not change native semantics, unless you really have to.</p> </blockquote> <p>As I mentioned, many HTML semantic elements have implicit meaning to them. When we use a <code>&lt;button&gt;</code>, for example, it is implied to user agents that this is an interactive element (interacted with through the cursor click, enter key, or space bar) that will trigger some interaction on the page. On the other hand, if we use a <code>&lt;a&gt;</code> element, it is implied to user agents that interacting with this element (through a cursor click or enter key) will navigate the user away from the page, or to a different location on the same page.</p> <p>Because of the implicit meaning that many of these elements have, it is advised that we do not change them with ARIA roles.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Heading Button<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span></code></pre> <div class="code-addendum warning">Incorrect method</div> <p>Instead of repurposing semantic elements, we should either use the appropriate element.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span><span class="token punctuation">></span></span>Heading Button<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span></code></pre> <div class="code-addendum success">Preferred method</div> <p>Or, as a last resort, we can apply the ARIA role to an element that doesn't carry such meaning.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>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>button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Heading Button<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span></code></pre> <h2 id="3-interactive-aria-elements-must-be-accessible-by-all-mediums" tabindex="-1">3. Interactive ARIA Elements must be Accessible by all Mediums <a class="header-anchor" href="https://bitsofco.de/rules-for-using-aria-in-html/">#</a></h2> <blockquote> <p>All interactive ARIA controls must be usable with the keyboard.</p> </blockquote> <p>Using an ARIA role on an element is not enough to really change the role of an element. If we are trying to change, for example, a <code>&lt;div&gt;</code> to a <code>&lt;button&gt;</code>, we need to manually add the interaction capabilities appropriate for a <code>&lt;button&gt;</code>.</p> <p>In the ARIA Guidelines, there is a list of capabilities each element should have. For example, a valid button must satisfy the following requirements -</p> <ul> <li>Must be clickable with a cursor</li> <li>Must be clickable with the enter key</li> <li>Must be clickable with the space bar</li> </ul> <p>When using ARIA roles, we need to be aware of these requirements. Making an element look like a button doesn't make it one. We need to take into account how users in all mediums interact with the element.</p> <h2 id="4-use-appropriate-roles-for-visible-focusable-elements" tabindex="-1">4. Use Appropriate Roles for Visible Focusable Elements <a class="header-anchor" href="https://bitsofco.de/rules-for-using-aria-in-html/">#</a></h2> <blockquote> <p>Do not use <code>role=&quot;presentation&quot;</code> or <code>aria-hidden=&quot;true&quot;</code> on a visible focusable element .</p> </blockquote> <p>The ARIA attribute, <code>role=&quot;presentation&quot;</code> implies that the element is for visual purposes only, and is not interactive in any way. The attribute, <code>aria-hidden=&quot;true&quot;</code> implies that the element should be not be visible.</p> <p>When we use these attributes, we need to be aware of the elements they are being applied to and what their visibility and interactive states are. For example, both these buttons will result in some users focusing on an element that doesn't exist to them.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</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>presentation<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">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<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">&lt;/</span>button</span><span class="token punctuation">></span></span></code></pre> <div class="code-addendum warning">Incorrect method</div> <p>These attributes should only be applied to elements who are non-interactive or not visible.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</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>presentation<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>Don't Click Me<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<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> none<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Don't Click Me<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></code></pre> <h2 id="5-interactive-elements-must-have-an-accessible-name" tabindex="-1">5. Interactive Elements must have an Accessible Name <a class="header-anchor" href="https://bitsofco.de/rules-for-using-aria-in-html/">#</a></h2> <blockquote> <p>All interactive elements must have an accessible name.</p> </blockquote> <p>Elements that can be interacted with, for example buttons and links, needs to have an &quot;Accessible Name&quot;. The Accessible Name is determined when the Accessibility API <code>accessible name</code> property has a valid value.</p> <p>The Accessible Name for an element can be specified depending on the type of element it is. Form inputs, for example, typically get their Accessible Name from a corresponding <code>&lt;label&gt;</code> element (see <a href="https://bitsofco.de/labelling-form-elements">Labelling Form Elements</a>).</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span><br> Username<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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 punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>password<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Password<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>password<span class="token punctuation">"</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>password<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>Other elements, for example buttons and links, can get their Accessible Name from their text content, or a label attribute (See <a href="https://bitsofco.de/html-for-screen-readers-labelling-elements">HTML for Screen Readers</a>).</p> Understanding the Critical Rendering Path 2017-01-17T00:00:00Z https://bitsofco.de/understanding-the-critical-rendering-path/ <p>When a browser receives the HTML response for a page from the server, there are a lot of steps to be taken before pixels are drawn on the screen. This sequence the browsers needs to run through for the initial paint of the page is called the &quot;Critical Rendering Path&quot;.</p> <p>Knowledge of the CRP is incredibly useful for understanding how a site's performance can be improved. There are 6 stages to the CRP -</p> <ol> <li>Constructing the DOM Tree</li> <li>Constructing the CSSOM Tree</li> <li>Running JavaScript</li> <li>Creating the Render Tree</li> <li>Generating the Layout</li> <li>Painting</li> </ol> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/hHXTTNWybz-742.avif 742w"><source type="image/webp" srcset="https://bitsofco.de/img/hHXTTNWybz-742.webp 742w"><img alt="Diagram of the Critical Rendering Path Sequence" loading="lazy" decoding="async" src="https://bitsofco.de/img/hHXTTNWybz-742.png" width="742" height="214"></picture></p> <h2 id="1-constructing-the-dom-tree" tabindex="-1">1. Constructing the DOM Tree <a class="header-anchor" href="https://bitsofco.de/understanding-the-critical-rendering-path/">#</a></h2> <p>The DOM (<a href="https://www.w3.org/DOM/">Document Object Model</a>) Tree is an Object representation of the fully parsed HTML page. Starting with the root element, <code>&lt;html&gt;</code>, nodes are created for each element/text on the page. Elements nested within other elements are represented as child nodes and each node contains the full attributes for that element. For example, an <code>&lt;a&gt;</code> element will have the <code>href</code> attribute associated with it’s node.</p> <p>Take, for example, this sample document -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">></span></span>Understanding the Critical Rendering Path<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>stylesheet<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>style.css<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">&lt;/</span>head</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Understanding the Critical Rendering Path<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Introduction<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>footer</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>small</span><span class="token punctuation">></span></span>Copyright 2017<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>small</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>footer</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre> <p>This will create the following DOM Tree -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/5B27_mbNcP-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/5B27_mbNcP-780.webp 780w"><img alt="DOM Tree" loading="lazy" decoding="async" src="https://bitsofco.de/img/5B27_mbNcP-780.png" width="780" height="411"></picture></p> <p>A good thing about HTML is that it can be executed in parts. The full document doesn't have to be loaded for content to start appearing on the page. However, other resources, CSS and JavaScript, can block the render of the page.</p> <h2 id="2-constructing-the-cssom-tree" tabindex="-1">2. Constructing the CSSOM Tree <a class="header-anchor" href="https://bitsofco.de/understanding-the-critical-rendering-path/">#</a></h2> <p>The CSSOM (<a href="https://www.w3.org/TR/cssom-1/">CSS Object Model</a>) is an Object representation of the styles associated with the DOM. It is represented in a similar way to the DOM, but with the associated styles for each node, whether they explicitly declared or implicitly inherited, included.</p> <p>In the <code>style.css</code> file from the document mentioned above, we have the folowing styles -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">body</span> <span class="token punctuation">{</span> <span class="token property">font-size</span><span class="token punctuation">:</span> 18px<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><br><span class="token selector">header</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> plum<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">h1</span> <span class="token punctuation">{</span> <span class="token property">font-size</span><span class="token punctuation">:</span> 28px<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><br><span class="token selector">main</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> firebrick<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">h2</span> <span class="token punctuation">{</span> <span class="token property">font-size</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><br><span class="token selector">footer</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p>This will create the following CSSOM Tree -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/lzOrOXZCOh-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/lzOrOXZCOh-780.webp 780w"><img alt="CSSOM Tree" loading="lazy" decoding="async" src="https://bitsofco.de/img/lzOrOXZCOh-780.png" width="780" height="411"></picture></p> <p>CSS is considered a <strong>&quot;render blocking resource&quot;</strong>. This means that the <a href="https://bitsofco.de/understanding-the-critical-rendering-path/">Render Tree (see below)</a> cannot be constructed without first fully parsing the resource. Unlike HTML, CSS cannot be used in parts because of its inherit cascading nature. Styles defined later in the document can override and change styles that were previously defined. So, if we start using CSS styles defined earlier in the stylesheet before the entirety of the stylesheet has been parsed, we may get a situation where the wrong CSS is being applied. This means that CSS must be fully parsed before we can move on to the next stage.</p> <p>CSS files are only considered render blocking if they apply to the current device. The <code>&lt;link rel=&quot;stylesheet&quot;&gt;</code> tag can accept a <code>media</code> attribute, in which we can specify any media query which the styles within apply to. If, for example, we have a stylesheet with a media attribute of <code>orientation:landscape</code>, and we are viewing the page in portrait mode, that resource will not be considered render blocking.</p> <p>CSS can also be <strong>&quot;script blocking&quot;</strong>. This is because JavaScript files must wait until the CSSOM has been constructed before it can run.</p> <h2 id="3-running-javascript" tabindex="-1">3. Running JavaScript <a class="header-anchor" href="https://bitsofco.de/understanding-the-critical-rendering-path/">#</a></h2> <p>JavaScript is considered a <strong>&quot;parser blocking resource&quot;</strong>. This means that the parsing of the HTML document itself is blocked by JavaScript.</p> <p>When the parser reaches a <code>&lt;script&gt;</code> tag, whether that be internal or external, it stops to fetch (if it is external) and run it. This why, if we have a JavaScript file that references elements within the document, it must be placed after the appearance of that document.</p> <p>To avoid JavaScript being parser blocking, it can be loaded asynchronously be applying the <code>async</code> attribute.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token operator">&lt;</span>script <span class="token keyword">async</span> src<span class="token operator">=</span><span class="token string">"script.js"</span><span class="token operator">></span></code></pre> <h2 id="4-creating-the-render-tree" tabindex="-1">4. Creating the Render Tree <a class="header-anchor" href="https://bitsofco.de/understanding-the-critical-rendering-path/">#</a></h2> <p>The Render Tree is a combination of both the DOM and CSSOM. It is a Tree that represents what will be eventually rendered on the page. This means that it only captures the visible content and will not include, for example, elements that have been hidden with CSS using <code>display: none</code>.</p> <p>Using the example DOM and CSSOM above, the following Render Tree will be created -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/aLy8p9Wxsu-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/aLy8p9Wxsu-780.webp 780w"><img alt="Render Tree" loading="lazy" decoding="async" src="https://bitsofco.de/img/aLy8p9Wxsu-780.png" width="780" height="411"></picture></p> <h2 id="5-generating-the-layout" tabindex="-1">5. Generating the Layout <a class="header-anchor" href="https://bitsofco.de/understanding-the-critical-rendering-path/">#</a></h2> <p>The Layout is what determines what the size of the viewport is, which provides context for CSS styles that are dependent on it, e.g. percentage or viewport units. The viewport size is determined by the meta viewport tag provided in the document head or, if no tag is provided, the default viewport width of 980px is used.</p> <p>For example, the most common meta viewport value is to set the viewport size to correspond to the device width -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width,initial-scale=1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>If the user visits the webpage on a device with a width of, for example, 1000px, then sizes will be based on that unit. Half the viewport will be 500px, 10vw will be 100px, and so on.</p> <h2 id="6-painting" tabindex="-1">6. Painting <a class="header-anchor" href="https://bitsofco.de/understanding-the-critical-rendering-path/">#</a></h2> <p>Finally, in the Painting step, the visible content of the page can be converted to pixels to be displayed on the screen.</p> <p>How much time the paint step takes depends on the size of the DOM, as well as what styles are applied. Some styles require more work to execute than others. For example, a complicated gradient background-image will require more time than a simple solid background colour.</p> <h2 id="putting-it-all-together" tabindex="-1">Putting it All Together <a class="header-anchor" href="https://bitsofco.de/understanding-the-critical-rendering-path/">#</a></h2> <p>To see the Critical Rendering Path in process, we can inspect it in DevTools. In Chrome, it is under the <strong>Timeline</strong> tab (in Canary, and soon to be Chrome stable, it's renamed <strong>Performance</strong>).</p> <p>Take for example, our sample HTML from above (with an added <code>&lt;script&gt;</code> tag) -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">></span></span>Understanding the Critical Rendering Path<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>stylesheet<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>style.css<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">&lt;/</span>head</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Understanding the Critical Rendering Path<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Introduction<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>footer</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>small</span><span class="token punctuation">></span></span>Copyright 2017<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>small</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>footer</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</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>main.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">&lt;/</span>script</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre> <p>If we look at the Event Log for the page load, this is what we get -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/acwrQDU2pD-568.avif 568w"><source type="image/webp" srcset="https://bitsofco.de/img/acwrQDU2pD-568.webp 568w"><img alt="Performance Timeline" loading="lazy" decoding="async" src="https://bitsofco.de/img/acwrQDU2pD-568.png" width="568" height="681"></picture></p> <ol> <li><strong>Send Request</strong> - GET request sent for index.html</li> <li><strong>Parse HTML</strong> and <strong>Send Request</strong> - Begin parsing of HTML and DOM construction. Send GET request for style.css and main.js</li> <li><strong>Parse Stylesheet</strong> - CSSOM created for style.css</li> <li><strong>Evaluate Script</strong> - Evaluate main.js</li> <li><strong>Layout</strong> - Generate Layout based on meta viewport tag in HTML</li> <li><strong>Paint</strong> - Paint pixels on document</li> </ol> <p>Based on this information, we can make decisions on how to optimize the Critical Rendering Path. I will go into some of these techniques in later articles.</p> 3 New CSS Features to Learn in 2017 2017-01-10T00:00:00Z https://bitsofco.de/3-new-css-features-to-learn-in-2017/ <p>Happy new year! 🎊</p> <p>With the new year, we have a whole new set of things to learn. Although there are many new features, these are 3 new CSS features I'm most excited about adopting this year.</p> <h2 id="1-feature-queries" tabindex="-1">1. Feature Queries <a class="header-anchor" href="https://bitsofco.de/3-new-css-features-to-learn-in-2017/">#</a></h2> <p>A while ago, I wrote about Feature Queries being <a href="https://bitsofco.de/the-one-css-feature">the one CSS feature I really want</a>. Well, now its basically here! It is now supported in every major browser (Opera Mini included) besides Internet Explorer.</p> <p>Feature Queries, using the <code>@supports</code> rule, allow us to wrap CSS in a conditional block that will only be applied if the current user agent supports a particular CSS property-value pair. A simple example of this is to only apply Flexbox styles to browsers that support <code>display: flex</code> -</p> <pre class="language-css" tabindex="0"><code class="language-css"><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> flex <span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p>Additionally, using operators like <code>and</code> and <code>not</code>, we can create even more complicated feature queries. For example, we can detect if a browser only supports the old Flexbox syntax -</p> <pre class="language-css" tabindex="0"><code class="language-css"><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> flexbox <span class="token punctuation">)</span><br> <span class="token keyword">and</span><br> <span class="token punctuation">(</span> <span class="token keyword">not</span> <span class="token punctuation">(</span> <span class="token property">display</span><span class="token punctuation">:</span> flex <span class="token punctuation">)</span> <span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> flexbox<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <h3 id="support" tabindex="-1">Support <a class="header-anchor" href="https://bitsofco.de/3-new-css-features-to-learn-in-2017/">#</a></h3> <p class="ciu_embed" data-feature="css-featurequeries" 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-featurequeries.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/css-featurequeries.png"> <img src="https://caniuse.bitsofco.de/image/css-featurequeries.jpg" alt="Data on support for the css-featurequeries feature across the major browsers from caniuse.com"> </picture> </p> <h2 id="2-grid-layout" tabindex="-1">2. Grid Layout <a class="header-anchor" href="https://bitsofco.de/3-new-css-features-to-learn-in-2017/">#</a></h2> <p>The <a href="https://drafts.csswg.org/css-grid/">CSS Grid Layout Module</a> defines a system for creating grid-based layouts. It has similarities with the <a href="https://www.w3.org/TR/css-flexbox-1/">Flexbible Box Layout Module</a>, but is more specifically designed for page layouts, and thus has a number of different features.</p> <h3 id="explicit-item-placement" tabindex="-1">Explicit Item Placement <a class="header-anchor" href="https://bitsofco.de/3-new-css-features-to-learn-in-2017/">#</a></h3> <p>A grid is made up of the Grid Container (created with <code>display: grid</code>), and Grid Items (it's children). In our CSS, we can easily and explicitly orgnise the placement and order of the grid items, independent of their placement in the markup.</p> <p>For example, in my article on <a href="https://bitsofco.de/holy-grail-layout-css-grid">The Holy Grail Layout with CSS Grid</a>, I showed how we can use this module to create the infamous &quot;holy grail layout&quot;.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/kNLXBGDfZK-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/kNLXBGDfZK-720.gif 720w"><img alt="Holy Grail Layout Demo" loading="lazy" decoding="async" src="https://bitsofco.de/img/kNLXBGDfZK-720.webp" width="720" height="446"></picture></p> <p>The underlying CSS was only 31 lines -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.hg__header</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> header<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.hg__footer</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> footer<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.hg__main</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> main<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.hg__left</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> navigation<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.hg__right</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> ads<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><br><span class="token selector">.hg</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-areas</span><span class="token punctuation">:</span> <span class="token string">"header header header"</span><br> <span class="token string">"navigation main ads"</span><br> <span class="token string">"footer footer footer"</span><span class="token punctuation">;</span><br> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> 150px 1fr 150px<span class="token punctuation">;</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 100px<br> 1fr<br> 30px<span class="token punctuation">;</span><br> <span class="token property">min-height</span><span class="token punctuation">:</span> 100vh<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 600px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.hg</span> <span class="token punctuation">{</span><br> <span class="token property">grid-template-areas</span><span class="token punctuation">:</span> <span class="token string">"header"</span><br> <span class="token string">"navigation"</span><br> <span class="token string">"main"</span><br> <span class="token string">"ads"</span><br> <span class="token string">"footer"</span><span class="token punctuation">;</span><br> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 100px<br> 50px<br> 1fr<br> 50px<br> 30px<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <h3 id="flexible-lengths" tabindex="-1">Flexible Lengths <a class="header-anchor" href="https://bitsofco.de/3-new-css-features-to-learn-in-2017/">#</a></h3> <p>The CSS Grid Module introduces a new length unit, the <code>fr</code> unit, which represents a fraction of the free space left in the grid container.</p> <p>This allows us to apportion heights and widths of grid items depending on the available space in the grid container. For example, in the Holy Grail Layout, I wanted the <code>main</code> section to take up all the remaining space after the two sidebars. To do that, I simply wrote -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.hg</span> <span class="token punctuation">{</span><br> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> 150px 1fr 150px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h3 id="gutters" tabindex="-1">Gutters <a class="header-anchor" href="https://bitsofco.de/3-new-css-features-to-learn-in-2017/">#</a></h3> <p>We can specifically define gutters for our grid layout using the <code>grid-row-gap</code>, <code>grid-column-gap</code>, and <code>grid-gap</code> properties. These properties accept a <a href="https://bitsofco.de/3-new-css-features-to-learn-in-2017/"><code>&lt;length-percentage&gt;</code> data type</a> as value, with the percentage corresponding to the dimension of the content area.</p> <p>For example, to have a 5% gutter, we would write -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.hg</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-column-gap</span><span class="token punctuation">:</span> 5%<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h3 id="support-1" tabindex="-1">Support <a class="header-anchor" href="https://bitsofco.de/3-new-css-features-to-learn-in-2017/">#</a></h3> <p>The CSS Grid Module will be available in browsers as early as March this year.</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="3-native-variables" tabindex="-1">3. Native Variables <a class="header-anchor" href="https://bitsofco.de/3-new-css-features-to-learn-in-2017/">#</a></h2> <p>Lastly, native CSS Variables (<a href="https://drafts.csswg.org/css-variables/">Custom Properties for Cascading Variables Module</a>). This module introduces a method for creating author-defined variables, which can be assigned as values to CSS properties.</p> <p>For example, if we have a theme colour we are using in several places in our stylesheet, we can abstract this out into a variable and reference that variable, instead of writing out the actual value multiple times.</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">--theme-colour</span><span class="token punctuation">:</span> cornflowerblue<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">h1</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--theme-colour<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">a</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--theme-colour<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">strong</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--theme-colour<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p>This is something we have been able to do with the help of CSS pre-processors like SASS, but CSS Variables have the advantage of living in the browser. This means that their values can be updated live. To change the <code>--theme-colour</code> property above, for example, all we have to do is the following -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> rootEl <span class="token operator">=</span> document<span class="token punctuation">.</span>documentElement<span class="token punctuation">;</span><br>rootEl<span class="token punctuation">.</span>style<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token string">'--theme-colour'</span><span class="token punctuation">,</span> <span class="token string">'plum'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <h2 id="support-2" tabindex="-1">Support <a class="header-anchor" href="https://bitsofco.de/3-new-css-features-to-learn-in-2017/">#</a></h2> <p class="ciu_embed" data-feature="css-variables" 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-variables.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/css-variables.png"> <img src="https://caniuse.bitsofco.de/image/css-variables.jpg" alt="Data on support for the css-variables feature across the major browsers from caniuse.com"> </picture> </p> <h2 id="what-about-support" tabindex="-1">What about Support? <a class="header-anchor" href="https://bitsofco.de/3-new-css-features-to-learn-in-2017/">#</a></h2> <p>As you can see, none of these features are fully supported in every browser yet, so how do we comfortably usem the in production? Well, Progressive Enhancement! Last year I gave a talk about how to apply Progressive Enhancement in relation to CSS at Fronteers Conference. You can watch the talk below -</p> <p><a href="https://player.vimeo.com/video/194815985"><picture><source type="image/avif" srcset="https://bitsofco.de/img/EpKDMJT6yy-956.avif 956w"><source type="image/webp" srcset="https://bitsofco.de/img/EpKDMJT6yy-956.webp 956w"><img alt="JavaScript Array Methods - Mutator" loading="lazy" decoding="async" src="https://bitsofco.de/img/EpKDMJT6yy-956.png" width="956" height="539"></picture></a></p> <p>What CSS features are you excited about to learn in 2017?</p> Making a Polymer Element for CanIUse Embed 2016-12-20T00:00:00Z https://bitsofco.de/caniuse-embed-polymer/ <p>About a year ago, I made an embed for <a href="https://caniuse.com">caniuse.com</a>., which pulls up-to-date data from the site into an embeddable widget, like this -</p> <p class="ciu_embed" data-feature="transforms2d" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false"> <picture> <source type="image/webp" srcset="https://caniuse.bitsofco.de/image/transforms2d.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/transforms2d.png"> <img src="https://caniuse.bitsofco.de/image/transforms2d.jpg" alt="Data on support for the transforms2d feature across the major browsers from caniuse.com"> </picture> </p> <p>You can read about how I built it in my article, <a href="https://bitsofco.de/caniuse-embed">Creating an Embed for CanIUse</a>. Lately, I have been interested in Polymer and Custom Elements, so I decided to make a Polymer element version of the embed.</p> <h2 id="how-it-works" tabindex="-1">How it Works <a class="header-anchor" href="https://bitsofco.de/caniuse-embed-polymer/">#</a></h2> <p>Before getting into how the Polymer element is used, we need to revisit how the embed itself works. The embed is an iframe that loads a particular document. This document is hosted at -</p> <pre><code>https://caniuse.bitsofco.de/embed/index.html </code></pre> <p>This url accepts 2 parameters -</p> <ul> <li><code>feat</code> - the ID of the feature on caniuse</li> <li><code>periods</code> - The browser versions to display, e.g. <code>current</code>,<code>past_1</code>, <code>future_1</code> etc.</li> </ul> <p>For example, for the &quot;CSS 2D Transforms&quot; feature, the embed is hosted at -</p> <pre><code>https://caniuse.bitsofco.de/embed/index.html?feat=transforms2d&amp;periods=future_1,current,past_1,past_2 </code></pre> <p>To use the plain JavaScript embed, you have to include a placeholder element in your document that contains three pieces of data -</p> <ul> <li>The feature slug</li> <li>The number of future browser versions to display</li> <li>The number of past browser version to display</li> </ul> <p>For example, this is a placeholder for the &quot;CSS 2D Transforms&quot; feature -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>transforms2d<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 punctuation">></span></span><br> <span class="token comment">&lt;!-- Placeholder/fallback content here --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre> <p>Finally, you have to include a script in your document that will load the actual iframe into the placeholder element.</p> <pre><code>&lt;script src=&quot;//cdn.jsdelivr.net/caniuse-embed/1.0.1/caniuse-embed.min.js&quot;&gt;&lt;/script&gt; </code></pre> <h3 id="how-the-polymer-element-works" tabindex="-1">How the Polymer Element Works <a class="header-anchor" href="https://bitsofco.de/caniuse-embed-polymer/">#</a></h3> <p>The Polymer element replaces the plain script a user would typically include, that loads the iframe onto the page. Instead, to use the Polymer element, a user would import it onto their page and use it in the following way -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>caniuse-embed</span> <span class="token attr-name">feature</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>transforms2d<span class="token punctuation">"</span></span> <span class="token attr-name">future-versions</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 attr-name">past-versions</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>caniuse-embed</span><span class="token punctuation">></span></span></code></pre> <p>The Polymer element then loads the iframe the same way the plain script does.</p> <h2 id="creating-the-polymer-element" tabindex="-1">Creating the Polymer Element <a class="header-anchor" href="https://bitsofco.de/caniuse-embed-polymer/">#</a></h2> <p>The entirety of a custom Polymer element exists in a single HTML file, within a <code>&lt;dom-module&gt;</code> element. Inside this <code>&lt;dom-module&gt;</code>, we have the HTML template, styles, and scripts associated with the custom element.</p> <p>The most basic version of the <code>&lt;caniuse-embed&gt;</code> element, which will just display &quot;Hello, World&quot;, looks like this -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token comment">&lt;!-- Import the Polymer library first --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>import<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>path/to/polymer/polymer.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dom-module</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>caniuse-embed<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">&lt;</span>template</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><span class="token comment">/* Custom styles here*/</span></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span><br><br> <span class="token comment">&lt;!-- HTML Template --></span><br> Hello, world!<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br> <span class="token comment">// Initialise Polymer element</span><br> <span class="token function">Polymer</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">is</span><span class="token operator">:</span> <span class="token string">'caniuse-embed'</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">&lt;/</span>script</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dom-module</span><span class="token punctuation">></span></span></code></pre> <p>To use this element in a page, we just need to import it into the document, then use the element like any other HTML element.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>import<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>path/to/caniuse-embed.html<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">&lt;/</span>head</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>caniuse-embed</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>caniuse-embed</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre> <p>This will display the template, defined in the <code>caniuse-embed.html</code> -</p> <pre><code>Hello, world! </code></pre> <h3 id="adding-properties" tabindex="-1">Adding Properties <a class="header-anchor" href="https://bitsofco.de/caniuse-embed-polymer/">#</a></h3> <p>Now that we have created the element, we can start adding properties to it. To add properties to an element, we add them to <code>properties</code> in the <code>Polymer</code> object, specifying any details for the property.</p> <p>For the <code>&lt;caniuse-embed&gt;</code>, there are three properties - <code>feature</code>, <code>futureVersions</code>, and <code>pastVersions</code>.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token function">Polymer</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">is</span><span class="token operator">:</span> <span class="token string">'caniuse-embed'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">properties</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">feature</span><span class="token operator">:</span> String<span class="token punctuation">,</span><br> <span class="token literal-property property">futureVersions</span><span class="token operator">:</span> String<span class="token punctuation">,</span><br> <span class="token literal-property property">pastVersions</span><span class="token operator">:</span> String<br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p>When using the property, users can add values to them by adding them as attributes to the element. Note that properties that are written in camelCase are converted to kebab-case when being used as properties.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>caniuse-embed</span> <span class="token attr-name">feature</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport-units<span class="token punctuation">"</span></span> <span class="token attr-name">future-versions</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 attr-name">past-versions</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>2<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>caniuse-embed</span><span class="token punctuation">></span></span></code></pre> <p>We can access the values of properties in our template by wrapping it in <code>[[ propertyName ]]</code>, which signifies data binding.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">></span></span><br>Feature: [[feature]]<br>Future Versions: [[futureVersions]]<br>Past Versions: [[pastVersions]]<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">></span></span></code></pre> <p>We can also access the values of properties in the script, using <code>this.propertyName</code> -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token operator">&lt;</span>script<span class="token operator">></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>feature<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>futureVersions<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>pastVersions<span class="token punctuation">)</span><br><span class="token operator">&lt;</span><span class="token operator">/</span>script<span class="token operator">></span></code></pre> <h3 id="styling-the-element" tabindex="-1">Styling the Element <a class="header-anchor" href="https://bitsofco.de/caniuse-embed-polymer/">#</a></h3> <p>We add styling to our custom element in the <code>&lt;style&gt;</code> block within the template. To style the actual element created, in this case <code>&lt;caniuse-embed&gt;</code>, we use the <code>:host</code> selector.</p> <p>For the caniuse embed, I was just loading an iframe, so I didn't need to add much styling, besides setting the <code>&lt;caniuse-embed&gt;</code> to full-width.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">:host</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 property">width</span><span class="token punctuation">:</span> 100%<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">&lt;/</span>style</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">></span></span></code></pre> <h3 id="loading-the-embed" tabindex="-1">Loading the Embed <a class="header-anchor" href="https://bitsofco.de/caniuse-embed-polymer/">#</a></h3> <p>Finally, I need to actually load the iframe into the template with the appropriate url. There are two ways I could do this</p> <ul> <li>Have the iframe already in the template, and use data binding to add the properties to the particular parts of the url</li> <li>Create the iframe using javascript and insert it into the template</li> </ul> <p>For this embed, I chose to use the latter method. This is because, the way in which the properties are used to build the url, is not the same way in which they are passed to the template. This means that, for the <code>pastVersions</code> and <code>futureVersions</code> properties in particular, I needed to convert that information into the way in which the embed expects in the url.</p> <p>To do this, I used the <code>ready()</code> method, which executes a function when the element is ready.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token function">Polymer</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">is</span><span class="token operator">:</span> <span class="token string">'caniuse-embed'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">properties</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 function-variable function">ready</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Custom function to convert the two properties into the url-ready string</span><br> <span class="token keyword">const</span> periods <span class="token operator">=</span> <span class="token function">calculatePeriods</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>futureVersions<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>pastVersions<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">this</span><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> &lt;iframe src="//caniuse.bitsofco.de/embed/index.html?feat=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>feature<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&amp;periods=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>periods<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"<br> frameborder="0"<br> width="100%"<br> height="400px">&lt;/iframe><br> </span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <h3 id="putting-it-all-together" tabindex="-1">Putting it all Together <a class="header-anchor" href="https://bitsofco.de/caniuse-embed-polymer/">#</a></h3> <p>As I mentioned, the entirety of the custom element is defined in one HTML file, <code>caniuse-embed.html</code>. This is what that file looks like -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>import<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>../polymer/polymer.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dom-module</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>caniuse-embed<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">:host</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 property">width</span><span class="token punctuation">:</span> 100%<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">&lt;/</span>style</span><span class="token punctuation">></span></span><br><br> <span class="token comment">&lt;!-- HTML added in ready() --></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"><br> <span class="token function">Polymer</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">is</span><span class="token operator">:</span> <span class="token string">'caniuse-embed'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">properties</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">feature</span><span class="token operator">:</span> String<span class="token punctuation">,</span><br> <span class="token literal-property property">futureVersions</span><span class="token operator">:</span> String<span class="token punctuation">,</span><br> <span class="token literal-property property">pastVersions</span><span class="token operator">:</span> String<br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token function-variable function">ready</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>feature <span class="token punctuation">)</span> <span class="token keyword">this</span><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">A feature was not included. Go to &lt;a href='https://caniuse.bitsofco.de/#polymer'>https://caniuse.bitsofco.de/#polymer&lt;/a> to get help.</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br><br> <span class="token keyword">const</span> periods <span class="token operator">=</span> <span class="token function">calculatePeriods</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>futureVersions<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>pastVersions<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">this</span><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> &lt;iframe src="//caniuse.bitsofco.de/embed/index.html?feat=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>feature<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&amp;periods=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>periods<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">"<br> frameborder="0"<br> width="100%"<br> height="400px">&lt;/iframe><br> </span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><br> <span class="token keyword">function</span> <span class="token function">calculatePeriods</span><span class="token punctuation">(</span><span class="token parameter">futureVersions<span class="token punctuation">,</span> pastVersions</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">// Logic to handle re-sizing of iFrame here</span><br><br> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dom-module</span><span class="token punctuation">></span></span></code></pre> <p>If you would like to see the full source for the element, you can see it on my GitHub repository, <a href="https://github.com/ireade/caniuse-embed-polymer">ireade/caniuse-embed-polymer</a>. There, you can also find details on how to use it in your website or application.</p> An Overview of Client-Side Storage 2016-12-13T00:00:00Z https://bitsofco.de/an-overview-of-client-side-storage/ <p>Storing data directly within the browser has a lot of benefits, the main one being quick and network-independent access to a &quot;database&quot;. There are currently four active methods (plus one deprecated), for storing data on the client side -</p> <ol> <li>Cookies</li> <li>Local Storage</li> <li>Session Storage</li> <li>IndexedDB</li> <li>WebSQL (deprecated)</li> </ol> <h2 id="cookies" tabindex="-1">Cookies <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h2> <p>Cookies are the classic way of storing simple string data within a document. Typically, cookies are sent from the server to the client, which can then store it, and send it back to the server on subsequent requests. This can be used for things like managing account sessions and tracking user information.</p> <p>Additionally, cookies can be used for storing data purely on the client-side. Because of this, they have also been used for storing general data, such as user preferences.</p> <h3 id="basic-crud-with-cookies" tabindex="-1">Basic CRUD with Cookies <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h3> <p>We can create, read, update, and delete cookies using the following syntax -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Create</span><br>document<span class="token punctuation">.</span>cookie <span class="token operator">=</span> <span class="token string">"user_name=Ire Aderinokun"</span><span class="token punctuation">;</span><br>document<span class="token punctuation">.</span>cookie <span class="token operator">=</span> <span class="token string">"user_age=25;max-age=31536000;secure"</span><span class="token punctuation">;</span><br><br><span class="token comment">// Read (All)</span><br>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span> document<span class="token punctuation">.</span>cookie <span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Update</span><br>document<span class="token punctuation">.</span>cookie <span class="token operator">=</span> <span class="token string">"user_age=24;max-age=31536000;secure"</span><span class="token punctuation">;</span><br><br><span class="token comment">// Delete</span><br>document<span class="token punctuation">.</span>cookie <span class="token operator">=</span> <span class="token string">"user_name=Ire Aderinokun;expires=Thu, 01 Jan 1970 00:00:01 GMT"</span><span class="token punctuation">;</span></code></pre> <h3 id="advantages-of-cookies" tabindex="-1">Advantages of Cookies <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h3> <ul> <li>They can be used for communication with the server</li> <li>We can set when we want the cookie to expire automatically, instead of having to manually delete</li> </ul> <h3 id="disadvantages-of-cookies" tabindex="-1">Disadvantages of Cookies <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h3> <ul> <li>They add to the page load of the document</li> <li>They can only store a small amount of data</li> <li>They can only store Strings</li> <li>Potential <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#Security">security issues</a></li> <li>It is not the recommended method for client-side storage anymore since the introduction of the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API">Web Storage API</a> (Local and Session Storage)</li> </ul> <h3 id="support" tabindex="-1">Support <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h3> <p>Cookies have basic support in all major browsers.</p> <h2 id="local-storage" tabindex="-1">Local Storage <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h2> <p>Local Storage is one type of the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API">Web Storage API</a>, which is an API for storing key-value pairs of data within the browser. It arose as a solution to the issues with Cookies, by offering a more intuitive and secure API for storing simple data within the browser.</p> <p>Although technically we can only store strings in Local Storage, this can be worked around by storing stringified JSON. This allows us to store a bit more complex data in Local Storage than we can with Cookies.</p> <h3 id="basic-crud-with-local-storage" tabindex="-1">Basic CRUD with Local Storage <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h3> <p>We can create, read, update, and delete data to Local Storage using the following syntax -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Create</span><br><span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token punctuation">{</span> <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> <span class="token literal-property property">age</span><span class="token operator">:</span> <span class="token number">25</span> <span class="token punctuation">}</span><br>localStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'user'</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>user<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Read (Single)</span><br>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>localStorage<span class="token punctuation">.</span><span class="token function">getItem</span><span class="token punctuation">(</span><span class="token string">'user'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><br><br><span class="token comment">// Update</span><br><span class="token keyword">const</span> updatedUser <span class="token operator">=</span> <span class="token punctuation">{</span> <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> <span class="token literal-property property">age</span><span class="token operator">:</span> <span class="token number">24</span> <span class="token punctuation">}</span><br>localStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'user'</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>updatedUser<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Delete</span><br>localStorage<span class="token punctuation">.</span><span class="token function">removeItem</span><span class="token punctuation">(</span><span class="token string">'user'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <h3 id="advantages-of-local-storage" tabindex="-1">Advantages of Local Storage <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h3> <ul> <li>Offers a more simple intuitive interface to storing data (than Cookies)</li> <li>More secure for client-side storage (than Cookies)</li> <li>Allows for the storage of more data (than Cookies)</li> </ul> <h3 id="disadvantages-of-local-storage" tabindex="-1">Disadvantages of Local Storage <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h3> <ul> <li>Only allows the storage of Strings</li> </ul> <h3 id="support-1" tabindex="-1">Support <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h3> <p class="ciu_embed" data-feature="namevalue-storage" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false"> <picture> <source type="image/webp" srcset="https://caniuse.bitsofco.de/image/namevalue-storage.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/namevalue-storage.png"> <img src="https://caniuse.bitsofco.de/image/namevalue-storage.jpg" alt="Data on support for the namevalue-storage feature across the major browsers from caniuse.com"> </picture> </p> <h2 id="session-storage" tabindex="-1">Session Storage <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h2> <p>Session Storage is the second type of the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API">Web Storage API</a>. It is exactly the same as Local Storage, except that the data is only stored for the browser tab session. Once the user closes that browser tab, the data is cleared.</p> <h3 id="basic-crud-with-session-storage" tabindex="-1">Basic CRUD with Session Storage <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h3> <p>We can create, read, update, and delete data to Session Storage using the following syntax -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Create</span><br><span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token punctuation">{</span> <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> <span class="token literal-property property">age</span><span class="token operator">:</span> <span class="token number">25</span> <span class="token punctuation">}</span><br>sessionStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'user'</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>user<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Read (Single)</span><br>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span> <span class="token constant">JSON</span><span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>sessionStorage<span class="token punctuation">.</span><span class="token function">getItem</span><span class="token punctuation">(</span><span class="token string">'user'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><br><br><span class="token comment">// Update</span><br><span class="token keyword">const</span> updatedUser <span class="token operator">=</span> <span class="token punctuation">{</span> <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> <span class="token literal-property property">age</span><span class="token operator">:</span> <span class="token number">24</span> <span class="token punctuation">}</span><br>sessionStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'user'</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>updatedUser<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Delete</span><br>sessionStorage<span class="token punctuation">.</span><span class="token function">removeItem</span><span class="token punctuation">(</span><span class="token string">'user'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <h3 id="advantages-disadvantages-and-support-for-session-storage" tabindex="-1">Advantages, Disadvantages, and Support for Session Storage <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h3> <p>Same as for Local Storage.</p> <h2 id="indexeddb" tabindex="-1">IndexedDB <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h2> <p>IndexedDB is a much more complex and well-rounded solution for storing data in the browser. It is a &quot;low-level API for client-side storage of significant amounts of structured data&quot; (Mozilla). It is a JavaScript-based, object-oriented, database that allows us to easily store and retrieve data that has been indexed with a key.</p> <p>In my article on <a href="https://bitsofco.de/bitsofcode-pwa-part-2-instant-loading-with-indexeddb">Building a Progressive Web Application</a>, I went over in more detail how you can use IndexedDB to create an offline-first application.</p> <h3 id="basic-crud-with-indexeddb" tabindex="-1">Basic CRUD with IndexedDB <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h3> <p><small>Note: In all my examples, I uses Jake Archibald’s [IndexedDB Promised library](https://github.com/jakearchibald/idb) which offers a Promise-ified version of the IndexedDB methods.</small></p> <p>Using IndexedDB is more complicated than the other browser storage methods. Before we can create/read/update/delete any data, we need to first open up the database, creating any stores (which are like tables in a database) we need.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">OpenIDB</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> idb<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">'SampleDB'</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">upgradeDb</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> users <span class="token operator">=</span> upgradeDb<span class="token punctuation">.</span><span class="token function">createObjectStore</span><span class="token punctuation">(</span><span class="token string">'users'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">keyPath</span><span class="token operator">:</span> <span class="token string">'name'</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> <p>To create (or update) data within a store, we need to go through the following steps -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// 1. Open up the database</span><br><span class="token function">OpenIDB</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">db</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> dbStore <span class="token operator">=</span> <span class="token string">'users'</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 2. Open a new read/write transaction with the store within the database</span><br> <span class="token keyword">const</span> transaction <span class="token operator">=</span> db<span class="token punctuation">.</span><span class="token function">transaction</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">,</span> <span class="token string">'readwrite'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> store <span class="token operator">=</span> transaction<span class="token punctuation">.</span><span class="token function">objectStore</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 3. Add the data to the store</span><br> store<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</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">age</span><span class="token operator">:</span> <span class="token number">25</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 4. Complete the transaction</span><br> <span class="token keyword">return</span> transaction<span class="token punctuation">.</span>complete<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 retrieve data, we need to go through the following -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// 1. Open up the database</span><br><span class="token function">OpenIDB</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">db</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> dbStore <span class="token operator">=</span> <span class="token string">'users'</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 2. Open a new read-only transaction with the store within the database</span><br> <span class="token keyword">const</span> transaction <span class="token operator">=</span> db<span class="token punctuation">.</span><span class="token function">transaction</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> store <span class="token operator">=</span> transaction<span class="token punctuation">.</span><span class="token function">objectStore</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 3. Return the data</span><br> <span class="token keyword">return</span> store<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">'Ire Aderinokun'</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">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">item</span><span class="token punctuation">)</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>item<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p>Finally, to delete data, we need to do the following -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// 1. Open up the database</span><br><span class="token function">OpenIDB</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">db</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> dbStore <span class="token operator">=</span> <span class="token string">'users'</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 2. Open a new read/write transaction with the store within the database</span><br> <span class="token keyword">const</span> transaction <span class="token operator">=</span> db<span class="token punctuation">.</span><span class="token function">transaction</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">,</span> <span class="token string">'readwrite'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> store <span class="token operator">=</span> transaction<span class="token punctuation">.</span><span class="token function">objectStore</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 3. Delete the data corresponding to the passed key</span><br> store<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span><span class="token string">'Ire Aderinokun'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 4. Complete the transaction</span><br> <span class="token keyword">return</span> transaction<span class="token punctuation">.</span>complete<span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p>If you're interested in learning more about how to use IndexedDB, you can read my <a href="https://bitsofco.de/bitsofcode-pwa-part-2-instant-loading-with-indexeddb">article</a> showing how I used it in a Progressive Web Application.</p> <h3 id="advantages-of-indexeddb" tabindex="-1">Advantages of IndexedDB <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h3> <ul> <li>Can handle more complex, structured, data</li> <li>Can have multiple &quot;databases&quot; and &quot;tables&quot; within each &quot;database&quot;</li> <li>More allowance for storage</li> <li>More control over how we interact with it</li> </ul> <h3 id="disadvantages-of-indexeddb" tabindex="-1">Disadvantages of IndexedDB <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h3> <ul> <li>More complex to use than the Web Storage API</li> </ul> <h3 id="support-2" tabindex="-1">Support <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h3> <p class="ciu_embed" data-feature="indexeddb" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false"> <picture> <source type="image/webp" srcset="https://caniuse.bitsofco.de/image/indexeddb.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/indexeddb.png"> <img src="https://caniuse.bitsofco.de/image/indexeddb.jpg" alt="Data on support for the indexeddb feature across the major browsers from caniuse.com"> </picture> </p> <h2 id="websql" tabindex="-1">WebSQL <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h2> <p>WebSQL is an API for a relational database on the client, similar to SQLite. Since 2010, the W3C Web Applications Working Group has ceased working on the specification. <strong>It is no longer a part of HTML specification, and should not be used</strong>.</p> <h2 id="a-comparison" tabindex="-1">A Comparison <a class="header-anchor" href="https://bitsofco.de/an-overview-of-client-side-storage/">#</a></h2> <table> <thead> <tr> <th>Feature</th> <th>Cookies</th> <th>Local Storage</th> <th>Session Storage</th> <th>IndexedDB</th> </tr> </thead> <tbody> <tr> <td>Storage Limit</td> <td><a href="https://browsercookielimits.squawky.net/">~4KB</a></td> <td><a href="https://dev-test.nemikor.com/web-storage/support-test/">~5MB</a></td> <td><a href="https://dev-test.nemikor.com/web-storage/support-test/">~5MB</a></td> <td><a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Browser_storage_limits_and_eviction_criteria">Up to half of hard drive</a></td> </tr> <tr> <td>Persistent Data?</td> <td>Yes</td> <td>Yes</td> <td>No</td> <td>Yes</td> </tr> <tr> <td>Data Value Type</td> <td>String</td> <td>String</td> <td>String</td> <td>Any structured data</td> </tr> <tr> <td>Indexable?</td> <td>No</td> <td>No</td> <td>No</td> <td>Yes</td> </tr> </tbody> </table> Document Outlines in HTML 5.1 2016-12-06T00:00:00Z https://bitsofco.de/document-outlines-in-html-5-1/ <p>In one of my previous articles, <a href="https://bitsofco.de/using-heading-elements-to-create-a-document-outline">Using Heading Elements to Create a Document Outline</a>, I explained the importance of having valid outlines in an HTML page.</p> <blockquote> <p>The outline for an HTML document shows the structure of the content on the page. This is useful for user agents, who can use the outline to create, for example, a table of contents for the document. This can then be used by screen readers to help people better navigate the page.</p> </blockquote> <p>Last week, HTML 5.1 was officially released. There were a <a href="https://www.w3.org/TR/2016/REC-html51-20161101/changes.html#changes">number of interesting changes made</a>, two of which relate to how we create a valid document outline.</p> <ul> <li><strong>Removed</strong>: The use of nested <code>&lt;section&gt;</code> elements each with an <code>&lt;h1&gt;</code> to create an outline</li> <li><strong>Changed</strong>: <code>&lt;header&gt;</code> and <code>&lt;footer&gt;</code> elements can be nested, if each level is within a sectioning element</li> </ul> <h2 id="creating-document-outlines-with-nested-section-elements" tabindex="-1">Creating Document Outlines with Nested <code>&lt;section&gt;</code> Elements <a class="header-anchor" href="https://bitsofco.de/document-outlines-in-html-5-1/">#</a></h2> <p>In HTML 5.0, a new way of creating a document outline was introduced. It involved using only the <code>&lt;h1&gt;</code> heading rank, and instead nesting <code>&lt;section&gt;</code> elements to define nested sections within the document.</p> <p>Take, for example, this markup -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Heading Level One<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Heading Level Two<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Heading Level Two<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Heading Level Three<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span></code></pre> <p>Deprecated method</p> <p>Using that markup, it should have created the following document outline -</p> <pre><code>1. Heading Level One 1. Heading Level Two 2. Heading Level Two 1. Heading Level Three </code></pre> <p>In my <a href="https://bitsofco.de/using-heading-elements-to-create-a-document-outline">previous article</a>, I mentioned that this method was not currently recognised by user agents and should not be used yet. In HTML 5.1, <strong>this method has been removed from the specification completely</strong>.</p> <p>Now, the advised method for creating a document outline is to still make use of nested <code>&lt;section&gt;</code> elements, but in combination with the appropriate heading rank for each section. For example, to create the document outline above, we should use 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">&lt;</span>section</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Heading Level One<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Heading Level Two<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Heading Level Two<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">></span></span>Heading Level Three<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span></code></pre> <h2 id="nesting-header-and-footer-elements" tabindex="-1">Nesting <code>&lt;header&gt;</code> and <code>&lt;footer&gt;</code> elements <a class="header-anchor" href="https://bitsofco.de/document-outlines-in-html-5-1/">#</a></h2> <p>In HTML 5.0, <code>&lt;header&gt;</code> elements couldn’t be nested within <code>&lt;header&gt;</code> elements, and <code>&lt;footer&gt;</code> elements couldn’t be nested within <code>&lt;footer&gt;</code> elements.</p> <p>In HTML 5.1, this has been changed. Now, we can have nested <code>&lt;header&gt;</code> and <code>&lt;footer&gt;</code> elements, but only if they are within a new sectioning context, which is created by nesting them within a sectioning element. These are one of the following -</p> <ul> <li><code>&lt;article&gt;</code></li> <li><code>&lt;section&gt;</code></li> <li><code>&lt;aside&gt;</code></li> <li><code>&lt;nav&gt;</code></li> </ul> <p>This way, the <code>&lt;header&gt;</code> or <code>&lt;footer&gt;</code> element is always related to a unique sectioning element, such as <code>&lt;section&gt;</code>, and not just the <code>&lt;header&gt;</code> or <code>&lt;footer&gt;</code> element itself. For example, an <code>&lt;article&gt;</code> element can have a <code>&lt;header&gt;</code>, which has various <code>&lt;sections&gt;</code>, detailing different information about the article.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>article</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Creating a Document Outline in HTML 5.1<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>The Author<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Ire Aderinokun<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>address</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>address</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>The Publication<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>bitsofcode<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>address</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>address</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Introduction<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span></code></pre> <p>This markup will produce the following outline -</p> <pre><code>1. Creating a Document Outline in HTML 5.1 1. The Author 2. The Publication 3. Introduction </code></pre> Making "Blue Beanie Me" 2016-11-29T00:00:00Z https://bitsofco.de/making-blue-beanie-me/ <p><a href="https://bluebeanieday.tumblr.com/">Blue Beanie Day</a> (November 30th) is a day where web developers show support of web standards and accessibility by posting a selfie of themselves wearing a blue beanie. It was first instigated by <a href="https://twitter.com/zeldman">Jeffrey Zeldman</a> in 2006, who wrote a book about web standards and famously wears a blue beanie. Tomorrow is the 10th anniversary and I don’t own a blue beanie myself, so I decided to create a web app that will allow you to add a blue beanie to any selfie you upload.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/wToJxm88Ko-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/wToJxm88Ko-780.webp 780w"><img alt="bluebeanieme.bitsofco.de" loading="lazy" decoding="async" src="https://bitsofco.de/img/wToJxm88Ko-780.png" width="780" height="731"></picture></p> <p><a href="https://bluebeanieme.bitsofco.de/">bluebeanieme.bitsofco.de</a></p> <p>There are many parts to how I made this application. Here, I want to focus on the most important part - how I added the blue beanie to the user's photo and created a picture they could download themselves. I did this using the Canvas API.</p> <h2 id="canvas-api-101" tabindex="-1">Canvas API 101 <a class="header-anchor" href="https://bitsofco.de/making-blue-beanie-me/">#</a></h2> <p>The Canvas API is a Web API for interacting with the HTML <code>&lt;canvas&gt;</code> element, enabling us to draw graphics onto it using JavaScript. In order to start drawing, we first need a <code>&lt;canvas&gt;</code> element in our document -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>canvas</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>canvas<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>400<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>400<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>canvas</span><span class="token punctuation">></span></span></code></pre> <p>Next, we need to create a rendering context, for example &quot;2d&quot;, from the element.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> canvas <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'canvas'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> ctx <span class="token operator">=</span> canvas<span class="token punctuation">.</span><span class="token function">getContext</span><span class="token punctuation">(</span><span class="token string">'2d'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>Now we can start drawing on the canvas. There are many shapes and media elements we can draw onto a canvas, but for <a href="https://bluebeanieme.bitsofco.de/">Blue Beanie Me</a> I only made use of two - rectangles and images.</p> <h3 id="drawing-rectangles" tabindex="-1">Drawing Rectangles <a class="header-anchor" href="https://bitsofco.de/making-blue-beanie-me/">#</a></h3> <p>To draw a rectangle with a solid colour background, we use the <code>fillRect()</code> method. This method accepts four arguments, which are all required -</p> <ul> <li>The x-coordinate of the upper-left corner of the rectangle</li> <li>The y-coordinate of the upper-left corner of the rectangle</li> <li>The width of the rectangle, in pixels</li> <li>The height of the rectangle, in pixels</li> </ul> <p>For example, to draw a 50x50px rectangle in the top left corner of the canvas, we would write the following -</p> <pre class="language-js" tabindex="0"><code class="language-js">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> <span class="token number">50</span><span class="token punctuation">,</span> <span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <h3 id="drawing-images" tabindex="-1">Drawing Images <a class="header-anchor" href="https://bitsofco.de/making-blue-beanie-me/">#</a></h3> <p>To draw an image onto a canvas, we use the <code>drawImage()</code> method. This method takes up to nine arguments -</p> <ul> <li>The image, canvas, or video element we want to draw on the canvas (required)</li> <li>The x coordinate where to start clipping</li> <li>The y coordinate where to start clipping</li> <li>The width of the clipped image</li> <li>The height of the clipped image</li> <li>The x coordinate where to place the image on the canvas (required)</li> <li>The y coordinate where to place the image on the canvas (required)</li> <li>The width of the image to use (stretch or reduce the image)</li> <li>The height of the image to use (stretch or reduce the image)</li> </ul> <p>For example, to draw an image element, resized to 50x50px, to the top left corner of the canvas, we would write the following -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> imageElement <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'image'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>ctx<span class="token punctuation">.</span><span class="token function">drawImage</span><span class="token punctuation">(</span>imageElement<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">50</span><span class="token punctuation">,</span> <span class="token number">50</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <h2 id="making-blue-beanie-me" tabindex="-1">Making “Blue Beanie Me” <a class="header-anchor" href="https://bitsofco.de/making-blue-beanie-me/">#</a></h2> <p>If you would like to see the full source code for the application, it is available on my <a href="https://github.com/ireade/bluebeanieme">github</a>. Here, I will go over how I added the beanie to the user's selfie, allowed them to reposition it, then export the image.</p> <h3 id="calculating-the-size-of-the-beanie" tabindex="-1">Calculating the Size of the Beanie <a class="header-anchor" href="https://bitsofco.de/making-blue-beanie-me/">#</a></h3> <p>The first thing I had to do was to calculate the size that the beanie should be. I achieved this by asking the user to select two points on the image - their upper left forehead and upper right forehead (<a href="https://res.cloudinary.com/ireaderinokun/image/upload/v1480328193/bluebeanieme/new.gif">See a demo</a>). Based on this, I was able to draw a beanie that will fit between those two points.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">drawBeanie</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Draw the user uploaded image</span><br> <span class="token function">drawImage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">const</span> beanieWidth <span class="token operator">=</span> coordinates<span class="token punctuation">.</span>right<span class="token punctuation">.</span>x <span class="token operator">-</span> coordinates<span class="token punctuation">.</span>left<span class="token punctuation">.</span>x <span class="token operator">+</span> <span class="token number">40</span><span class="token punctuation">;</span><br> ctx<span class="token punctuation">.</span><span class="token function">drawImage</span><span class="token punctuation">(</span>beanie<span class="token punctuation">,</span> coordinates<span class="token punctuation">.</span>left<span class="token punctuation">.</span>x<span class="token punctuation">,</span> coordinates<span class="token punctuation">.</span>left<span class="token punctuation">.</span>y <span class="token operator">-</span> beanieWidth<span class="token punctuation">,</span> beanieWidth<span class="token punctuation">,</span> beanieWidth<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Note - The beanie image is a square, which is why height is the same as width</span><br><span class="token punctuation">}</span><br><br><span class="token keyword">function</span> <span class="token function">onCanvasClick</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Determine if the user has added coords</span><br> numberOfTimesClicked<span class="token operator">++</span><span class="token punctuation">;</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>numberOfTimesClicked <span class="token operator">===</span> <span class="token number">3</span><span class="token punctuation">)</span> numberOfTimesClicked <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span><br><br> <span class="token keyword">const</span> coords <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">x</span><span class="token operator">:</span> e<span class="token punctuation">.</span>offsetX<span class="token punctuation">,</span><br> <span class="token literal-property property">y</span><span class="token operator">:</span> e<span class="token punctuation">.</span>offsetY<br> <span class="token punctuation">}</span><br><br> <span class="token comment">// Set rectangle so user sees feedback</span><br> <span class="token function">addRect</span><span class="token punctuation">(</span>coords<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">switch</span> <span class="token punctuation">(</span>numberOfTimesClicked<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">case</span> <span class="token number">1</span><span class="token operator">:</span><br> <span class="token function">setLeftCoord</span><span class="token punctuation">(</span>coords<span class="token punctuation">)</span><br> <span class="token keyword">break</span><span class="token punctuation">;</span><br> <span class="token keyword">case</span> <span class="token number">2</span><span class="token operator">:</span><br> <span class="token function">setRightCoord</span><span class="token punctuation">(</span>coords<span class="token punctuation">)</span><br> <span class="token function">drawBeanie</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">break</span><span class="token punctuation">;</span><br> <span class="token keyword">default</span><span class="token operator">:</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></code></pre> <h3 id="repositioning-the-beanie" tabindex="-1">Repositioning the Beanie <a class="header-anchor" href="https://bitsofco.de/making-blue-beanie-me/">#</a></h3> <p>To allow the user to reposition the beanie, I created a set of buttons with custom data attributes to determine direction.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">data-direction</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>up<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Move Beanie Up<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token entity" title="&#8593;">&amp;#8593;</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">data-direction</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>left<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Move Beanie Left<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token entity" title="&#8592;">&amp;#8592;</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">data-direction</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>down<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Move Beanie Down<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token entity" title="&#8595;">&amp;#8595;</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">data-direction</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>right<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Move Beanie Right<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token entity" title="&#8594;">&amp;#8594;</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></code></pre> <p>When a user clicks on one of these buttons, I shift the beanie's coordinates accordingly, and redraw the entire canvas.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">moveBeanie</span><span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">const</span> direction <span class="token operator">=</span> e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>dataset<span class="token punctuation">.</span>direction<span class="token punctuation">;</span><br><br> <span class="token keyword">switch</span><span class="token punctuation">(</span>direction<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">case</span> <span class="token string">'left'</span><span class="token operator">:</span><br> coordinates<span class="token punctuation">.</span>left<span class="token punctuation">.</span>x <span class="token operator">--</span><br> coordinates<span class="token punctuation">.</span>right<span class="token punctuation">.</span>x <span class="token operator">--</span><br> <span class="token keyword">break</span><span class="token punctuation">;</span><br> <span class="token keyword">case</span> <span class="token string">'right'</span><span class="token operator">:</span><br> coordinates<span class="token punctuation">.</span>left<span class="token punctuation">.</span>x <span class="token operator">++</span><br> coordinates<span class="token punctuation">.</span>right<span class="token punctuation">.</span>x <span class="token operator">++</span><br> <span class="token keyword">break</span><span class="token punctuation">;</span><br> <span class="token keyword">case</span> <span class="token string">'down'</span><span class="token operator">:</span><br> coordinates<span class="token punctuation">.</span>left<span class="token punctuation">.</span>y <span class="token operator">++</span><br> coordinates<span class="token punctuation">.</span>right<span class="token punctuation">.</span>y <span class="token operator">++</span><br> <span class="token keyword">break</span><span class="token punctuation">;</span><br> <span class="token keyword">case</span> <span class="token string">'up'</span><span class="token operator">:</span><br> coordinates<span class="token punctuation">.</span>left<span class="token punctuation">.</span>y <span class="token operator">--</span><br> coordinates<span class="token punctuation">.</span>right<span class="token punctuation">.</span>y <span class="token operator">--</span><br> <span class="token keyword">break</span><span class="token punctuation">;</span><br> <span class="token keyword">default</span><span class="token operator">:</span><br> <span class="token keyword">break</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token comment">// Redraw beanie</span><br> <span class="token function">drawBeanie</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h3 id="exporting-the-image" tabindex="-1">Exporting the Image <a class="header-anchor" href="https://bitsofco.de/making-blue-beanie-me/">#</a></h3> <p>Finally, to allow the user to export the image, we use the <code>canvas.toDataURL()</code> method. This method returns a Data URI representing the image in any format and resolution we specify. By default, the returned image is a PNG at a resolution of 96 dpi.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">exportImage</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> canvas <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'canvas'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> exportedImage <span class="token operator">=</span> canvas<span class="token punctuation">.</span><span class="token function">toDataURL</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Open the image in a new window</span><br> window<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span>exportedImage<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>That's it! You can view the full source code for the app on my <a href="https://github.com/ireade/bluebeanieme">github</a>. Otherwise, make sure to <a href="https://bluebeanieme.bitsofco.de/">use the application</a> and don't forget <a href="https://www.zeldman.com/2016/11/22/year-ever-blue-beanie-day-matters/">why Blue Beanie Day is important</a>. If you're interested in reading about accessibility, you can check out all my <a href="https://bitsofco.de/the-accessibility-cheatsheet">articles related to accessibility</a>.</p> Highlights from Chrome Dev Summit 2016 2016-11-22T00:00:00Z https://bitsofco.de/chrome-dev-summit-2016/ <p>A couple of weeks ago I was lucky enough to be able to attend the <a href="https://developer.chrome.com/devsummit/">Chrome Dev Summit</a> in San Francisco. It was a really great experience to be able to see, in person, a lot of the people working on the APIs I use everyday, as well as to see how other developers are creating complex modern web applications.</p> <p>I would urge you to watch all the videos yourself (<a href="https://www.youtube.com/playlist?list=PLNYkxOF6rcIBTs2KPy1E6tIYaWoFcG3uj">Chrome Dev Summit 2016 Playlist</a>), but here are the top 5 things I found the most interesting/exciting.</p> <h2 id="web-payments" tabindex="-1">Web Payments <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2016/">#</a></h2> <p>Zach Koch (Product Manager, Chrome) spoke on the first day about <a href="https://developer.chrome.com/devsummit/schedule/sessions/web-payments">Web Payments</a>, and how to use the new <a href="https://www.w3.org/TR/payment-request/">Payment Request API</a>.</p> <p>The Payment Request API is a new web API, still in working draft, that is intended to eliminate checkout forms. It aims to provide a standardised and consistent method of making payments on the web for users, by moving the handling of retrieving payment information to the browser, instead of individual checkout forms on each website.</p> <p>Making a new payment request is as simple as writing the following -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">new</span> <span class="token class-name">PaymentRequest</span><span class="token punctuation">(</span><br> methodData<span class="token punctuation">,</span> <span class="token comment">// required payment method data</span><br> details<span class="token punctuation">,</span> <span class="token comment">// required information about transaction</span><br> options <span class="token comment">// optional parameter for things like shipping, etc.</span><br><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>This will trigger the browser UI for collecting payment information. If the user has used it before, they will have already filled information about their credit card and shipping address, so checking out can be as simple as one click. Then, the browser passes the response back to the site in a structured format. Here’s a demo of how it could work -</p> <p><a href="https://www.youtube.com/watch?v=hmqZxP6iTpo"><picture><source type="image/avif" srcset="https://bitsofco.de/img/GEnqOWTj-9-1276.avif 1276w"><source type="image/webp" srcset="https://bitsofco.de/img/GEnqOWTj-9-1276.webp 1276w"><img alt="Payment Request API Demo" loading="lazy" decoding="async" src="https://bitsofco.de/img/GEnqOWTj-9-1276.png" width="1276" height="714"></picture></a></p> <p>I find this new API really exciting because it's great for everyone. Users get a more seamless checkout process, and us as developers don't have to deal with creating tedious and complex checkout forms.</p> <h2 id="the-importance-of-testing-on-real-devices" tabindex="-1">The Importance of Testing on Real Devices <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2016/">#</a></h2> <p>Alex Russell (Software Engineer, Chrome) gave a presentation on <a href="https://developer.chrome.com/devsummit/schedule/sessions/progressive-performance">Progressive Performance</a>, in which he spoke about the best practices for building truly performant websites today.</p> <p>His talk was a real eye-opener about how mobile devices perform compared to desktops. An example he gave was the 2015 Google I/O site, which was a Progressive Web App built with Polymer. Using the mobile emulator on Chrome things were fast, with total JS time at less than 600ms and interactive content at about 4s. However, the same site on the same WiFi network loading on a Nexus 5 was significantly slower, with total JS time at about 4s and interactive content at about 7s!</p> <blockquote> <p>“Your Laptop is a Filthy Liar”</p> </blockquote> <p>The reason for this is that, no matter how much emulators may try to mimic real devices, they aren't running the same actual hardware, so they will never be actually accurate.</p> <p>Alex's solution to this is to test your sites on real devices, particularly mid-range circa 2014 devices. This will give you a more realistic representation of what your website is actually performing like for a good portion of your users.</p> <h2 id="using-progressive-web-apps-with-accelerated-mobile-pages" tabindex="-1">Using Progressive Web Apps with Accelerated Mobile Pages <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2016/">#</a></h2> <p>Paul Bakaus (Developer Advocate, Google) gave a talk titled, <a href="https://developer.chrome.com/devsummit/schedule/sessions/from-amp-to-pwa">From AMP to PWA - The Best of Both Worlds</a>. In this talk, he spoke about how to use Accelerated Mobile Pages with Progressive Web Apps to create the ultimate performance experience.</p> <p>In the development environment today where there are so many different frameworks and platforms to choose from, it often seems like you have to make a choice and only use one. But different frameworks have their advantages and disadvantages. AMP, for example, is great for discovery and has a lightening-fast loading time, even on first visit. However, it only allows serving of static content, no user scripts allowed. PWA, on the other hand, have very advanced features and dynamic content, but may not be as easily embedded or have as great discovery in search.</p> <blockquote> <p>AMP + PWA = PWAMP</p> </blockquote> <p>In his talk, Paul showed how you can leverage both platforms by using AMP as a data format, a technique he called &quot;AMP down&quot;. On first visit, users are served the super fast AMP page, and the Service Worker is installed in the background (Using <a href="https://www.ampproject.org/docs/reference/components/amp-install-serviceworker"><code>&lt;amp-install-serviceworker&gt;</code></a>). On subsequent pages, they are diverted to the PWA, which now loads as fast as if it would on a second visit!</p> <h2 id="progressive-web-apps-single-page-apps" tabindex="-1">Progressive Web Apps != Single Page Apps <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2016/">#</a></h2> <p>As is to be expected, Jake Archibald (Developer Advocate, Chrome) spoke about the Service Worker API in his talk, <a href="https://developer.chrome.com/devsummit/schedule/sessions/future-app-model">Future App Model: Advanced Service Worker</a>.</p> <p>In his talk, he challenged the notion that Progressive Web Apps need to be Single Page Apps. Although Single Page Apps been popular because of client-side frameworks and the advent of the App Shell Model, he demonstrated that we can get even faster page loads with server-side renderings by taking advantage of <a href="https://streams.spec.whatwg.org">Streams</a>.</p> <p>Streaming is a behaviour that happens natively on the web. As you may have noticed with regular server-rendered sites, the page content loads progressively. This is because the browser doesn't wait for everything to be received before it starts acting on it. This is behaviour is that is soon coming to the Fetch API. With Streams, we will be able to read the response from a fetch request as it is coming in, and handle how we display the information progressively.</p> <h2 id="navigation-transitions" tabindex="-1">Navigation Transitions <a class="header-anchor" href="https://bitsofco.de/chrome-dev-summit-2016/">#</a></h2> <p>In Jake Archibald's talk, <a href="https://developer.chrome.com/devsummit/schedule/sessions/future-app-model">Future App Model: Advanced Service Worker</a>, he spoke about a lot of speculative features that may be added to the Service Worker API.</p> <p>My favourite (and the most speculative) was an API for handling navigation transitions. Currently, in order for us to have fancy transitions between pages the way native mobile applications do, we have to use the Single Page Application model. The goal of this API is to give us control over transitions between pages even when the page is server rendered. For example, we should be able to achieve this in the Progressive Web App -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/e9P6G0Qq3B-862.avif 862w"><source type="image/gif" srcset="https://bitsofco.de/img/e9P6G0Qq3B-862.gif 862w"><img alt="Navigation Transitions Demo" loading="lazy" decoding="async" src="https://bitsofco.de/img/e9P6G0Qq3B-862.webp" width="862" height="475"></picture></p> <p><a href="https://developer.chrome.com/devsummit/schedule/sessions/future-app-model">Taken from Slides, &quot;Future App Model&quot;</a></p> <p>The way the API would work will be by introducing a new event, <code>navigate</code>, that we can listen for in the <code>window</code>. For example, a simple cross-fade transition could be written like this -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Listen for a new "navigate" event</span><br>window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'navigate'</span><span class="token punctuation">,</span> <span class="token parameter">event</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// Keep the current document alive until this Promise is resolved</span><br> event<span class="token punctuation">.</span><span class="token function">transitionUntil</span><span class="token punctuation">(</span><br><br> <span class="token comment">// Get the new window</span><br> event<span class="token punctuation">.</span>newWindow<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">newWin</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>newWin<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span><br><br> <span class="token comment">// assuming newWin.document.interactive means DOM ready</span><br> <span class="token keyword">return</span> newWin<span class="token punctuation">.</span>document<span class="token punctuation">.</span>interactive<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 keyword">return</span> newWin<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span><span class="token function">animate</span><span class="token punctuation">(</span><span class="token punctuation">[</span><br> <span class="token punctuation">{</span><span class="token literal-property property">opacity</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token literal-property property">opacity</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">}</span><br> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">.</span>finished<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><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><br><br><span class="token comment">// Example from https://github.com/jakearchibald/navigation-transitions</span></code></pre> <p>There were lots more interesting content at the summit, for example the <a href="https://developer.chrome.com/devsummit/schedule/sessions/how-we-built-it">Lyft team</a> sharing how they created a 40KB G-Zipped Progressive Web App 😱!If you're interested, you can <a href="https://www.youtube.com/playlist?list=PLNYkxOF6rcIBTs2KPy1E6tIYaWoFcG3uj">watch the recordings here</a>.</p> for..in versus for..of Loops 2016-11-15T00:00:00Z https://bitsofco.de/for-in-vs-for-of/ <p>The most basic type of iteration method in JavaScript is the <code>for</code> loop. It takes three expressions; a variable declaration, an expression to be evaluated before each iteration, and an expression to be evaluated at the end of each iteration. For example, this for loop will <code>console.log</code> each item in an array.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> array <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'a'</span><span class="token punctuation">,</span> <span class="token string">'b'</span><span class="token punctuation">,</span> <span class="token string">'c'</span><span class="token punctuation">,</span> <span class="token string">'d'</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">&lt;</span> array<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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>array<span class="token punctuation">[</span>i<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 comment">// Result: a, b, c, d</span></code></pre> <p>In addition to this <code>for</code> loop, there are two other types of <code>for</code> iteration methods we can use: <code>for..in</code> and <code>for..of</code>.</p> <h2 id="the-for-in-statement" tabindex="-1">The <code>for..in</code> Statement <a class="header-anchor" href="https://bitsofco.de/for-in-vs-for-of/">#</a></h2> <p><code>for..in</code> is a method for iterating over &quot;enumerable&quot; properties of an object. It therefore applies to all objects (not only <code>Object()</code>s) that have these properties.</p> <p>An enumerable property is defined as a property of an object that has an <code>Enumerable</code> value of true. Essentially, a property is &quot;enumerable&quot;, if it is <code>enumerable</code>. We can check if a property is enumerable by calling <code>property.enumerable</code>, which will return true or false.</p> <p>We use the <code>for..in</code> loop with the following syntax -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">for</span> <span class="token punctuation">(</span>variable <span class="token keyword">in</span> enumerable<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// do stuff</span><br><span class="token punctuation">}</span></code></pre> <p>For example, to loop through and <code>console.log</code> all the values in this Object, we can do the following -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> obj <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">a</span><span class="token operator">:</span> <span class="token number">1</span><span class="token punctuation">,</span><br> <span class="token literal-property property">b</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span><br> <span class="token literal-property property">c</span><span class="token operator">:</span> <span class="token number">3</span><span class="token punctuation">,</span><br> <span class="token literal-property property">d</span><span class="token operator">:</span> <span class="token number">4</span><br><span class="token punctuation">}</span><br><br><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> key <span class="token keyword">in</span> obj<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> obj<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token punctuation">)</span><br><span class="token punctuation">}</span><br><br><span class="token comment">// Result: 1, 2, 3, 4</span></code></pre> <p>The <code>for ... in</code> loop will iterate over inherited properties as well, as long as they are enumerable properties. The <code>for ... in</code> iteration happens in an arbitrary order. Therefore, it should not be used if things need to happen in their defined sequence.</p> <h3 id="for-in-and-objects" tabindex="-1"><code>for..in</code> and Objects <a class="header-anchor" href="https://bitsofco.de/for-in-vs-for-of/">#</a></h3> <p>The <code>for..in</code> method provides us the most straightforward way to loop over Object keys and values, since Objects do not have access to the <code>forEach</code> method that Arrays do.</p> <h3 id="for-in-and-arrays" tabindex="-1"><code>for..in</code> and Arrays <a class="header-anchor" href="https://bitsofco.de/for-in-vs-for-of/">#</a></h3> <p>The &quot;key&quot; for values in an Array are the numerical indexes. Therefore, these indexes are essentially just enumerable properties, like Object keys, except they are integers instead of strings.</p> <p>This means that we can loop over all the values in an Array by retrieving their index using the <code>for..in</code> Array.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> array <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'a'</span><span class="token punctuation">,</span> <span class="token string">'b'</span><span class="token punctuation">,</span> <span class="token string">'c'</span><span class="token punctuation">,</span> <span class="token string">'d'</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">const</span> index <span class="token keyword">in</span> array<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>array<span class="token punctuation">[</span>index<span class="token punctuation">]</span><span class="token punctuation">)</span><br><span class="token punctuation">}</span><br><br><span class="token comment">// Result: a, b, c, d</span></code></pre> <p>However, it is generally advised that <code>for..in</code> not be used with Arrays, particularly because it cannot be guaranteed that the iteration happens in sequence, which is usually important for Arrays.</p> <h3 id="for-in-and-strings" tabindex="-1"><code>for..in</code> and Strings <a class="header-anchor" href="https://bitsofco.de/for-in-vs-for-of/">#</a></h3> <p>Each character in a string has an index. Therefore, similar to Arrays, the indexes are enumerable properties that just happen to be integers.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> string <span class="token operator">=</span> <span class="token string">'Ire Aderinokun'</span><span class="token punctuation">;</span><br><br><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> index <span class="token keyword">in</span> string<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>string<span class="token punctuation">[</span>index<span class="token punctuation">]</span><span class="token punctuation">)</span><br><span class="token punctuation">}</span><br><br><span class="token comment">// Result: I, r, e, , A, d, e, r, i, n, o, k, u, n</span></code></pre> <h2 id="the-for-of-statement" tabindex="-1">The <code>for..of</code> Statement <a class="header-anchor" href="https://bitsofco.de/for-in-vs-for-of/">#</a></h2> <p><code>for..of</code> is a method, introduced in ES2015, for iterating over &quot;iterable collections&quot;. These are objects that have a <code>[Symbol.iterator]</code> property.</p> <p>The <code>[Symbol.iterator]</code> property allows us to manually iterate over the collection by calling the <code>[Symbol.iterator]().next()</code> method to retrieve the next item in the collection.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> array <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'a'</span><span class="token punctuation">,</span><span class="token string">'b'</span><span class="token punctuation">,</span><span class="token string">'c'</span><span class="token punctuation">,</span> <span class="token string">'d'</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> iterator <span class="token operator">=</span> array<span class="token punctuation">[</span>Symbol<span class="token punctuation">.</span>iterator<span class="token punctuation">]</span><span class="token punctuation">(</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> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>value <span class="token punctuation">)</span><br>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>value <span class="token punctuation">)</span><br>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>value <span class="token punctuation">)</span><br>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span> iterator<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>value <span class="token punctuation">)</span><br><br><span class="token comment">// Result: a, b, c, d</span></code></pre> <p>The <code>for..of</code> syntax is essentially a wrapper around the <code>[Symbol.iterator]</code> to create loops. It uses the following syntax -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">for</span> <span class="token punctuation">(</span>variable <span class="token keyword">of</span> iterable<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// do stuff</span><br><span class="token punctuation">}</span></code></pre> <h3 id="for-of-and-objects" tabindex="-1"><code>for..of</code> and Objects <a class="header-anchor" href="https://bitsofco.de/for-in-vs-for-of/">#</a></h3> <p>The <code>for..of</code> loop doesn't work with Objects because they are not &quot;iterable&quot;, and therefore don't have a <code>[Symbol.iterator]</code> property.</p> <h3 id="for-of-and-arrays-strings" tabindex="-1"><code>for..of</code> and Arrays/Strings <a class="header-anchor" href="https://bitsofco.de/for-in-vs-for-of/">#</a></h3> <p>The <code>for..of</code> loop works well with Arrays and Strings, as they are iterable. This method is a more reliable way of looping through an Array in sequence.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> array <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'a'</span><span class="token punctuation">,</span> <span class="token string">'b'</span><span class="token punctuation">,</span> <span class="token string">'c'</span><span class="token punctuation">,</span> <span class="token string">'d'</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> item <span class="token keyword">of</span> array<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>item<span class="token punctuation">)</span><br><span class="token punctuation">}</span><br><span class="token comment">// Result: a, b, c, d</span><br><br><span class="token keyword">const</span> string <span class="token operator">=</span> <span class="token string">'Ire Aderinokun'</span><span class="token punctuation">;</span><br><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">const</span> character <span class="token keyword">of</span> string<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>character<span class="token punctuation">)</span><br><span class="token punctuation">}</span><br><span class="token comment">// Result: I, r, e, , A, d, e, r, i, n, o, k, u, n</span></code></pre> <h3 id="for-of-and-nodelists" tabindex="-1"><code>for..of</code> and NodeLists <a class="header-anchor" href="https://bitsofco.de/for-in-vs-for-of/">#</a></h3> <p>Finally, another really useful case for <code>for..of</code> is in iterating of NodeLists. When we query the document for a group of elements, what we get returned is a NodeList, not an Array. This means that we can't iterate over the list using Array methods like <code>forEach</code>.</p> <p>To solve this, we can either convert it to an Array using <code>Array.from()</code>, or use the <code>for..of</code> loop, which is applicable to more than just Arrays.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> elements <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'.foo'</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">const</span> element <span class="token keyword">of</span> elements<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> element<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> doSomething<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="a-comparison" tabindex="-1">A Comparison <a class="header-anchor" href="https://bitsofco.de/for-in-vs-for-of/">#</a></h2> <table> <thead> <tr> <th> </th> <th><code>for..in</code></th> <th><code>for..of</code></th> </tr> </thead> <tbody> <tr> <td>Applies to</td> <td>Enumerable Properties</td> <td>Iterable Collections</td> </tr> <tr> <td>Use with Objects?</td> <td>Yes</td> <td>No</td> </tr> <tr> <td>Use with Arrays?</td> <td>Yes, but not advised</td> <td>Yes</td> </tr> <tr> <td>Use with Strings?</td> <td>Yes, but not advised</td> <td>Yes</td> </tr> </tbody> </table> Challenge - Recreating the iTunes Library 2016-11-08T00:00:00Z https://bitsofco.de/challenge-itunes-library/ <p>It's time for another UI challenge! In the <a href="https://bitsofco.de/clear-js">first challenge</a>, we recreated some interactions from the MacOS application, Clear. In this one, we are recreating the look of the old iTunes library gallery.</p> <h2 id="the-challenge" tabindex="-1">The Challenge <a class="header-anchor" href="https://bitsofco.de/challenge-itunes-library/">#</a></h2> <video autoplay="" loop="" muted="" playsinline="" poster="https://res.cloudinary.com/ireaderinokun/image/upload/v1485711823/lossy-compressed_s9cypz.jpg"> <source type="video/webm" src="https://res.cloudinary.com/ireaderinokun/image/upload/v1485711823/lossy-compressed_s9cypz.webm"> <img src="https://res.cloudinary.com/ireaderinokun/image/upload/v1485711823/lossy-compressed_s9cypz.gif"> </video> <p class="img-subheading"> <a href="https://codepen.io/ire/pen/xEvrNR">Start the Challenge</a> | <a href="https://github.com/ireade/ui-challenges/tree/gh-pages/2-itunes-library">Live Demo with My Solution</a> </p> <p>The key interactions we are trying to recreate -</p> <ul> <li>The gallery of “albums”/“cards” should stacked horizontally.</li> <li>When inactive, each card should be arranged “stacked” as shown above</li> <li>When active, the card should be displayed in full view.</li> <li>When the user hovers over a card, it should be slightly enlarged. When they click on the card, it should become active.</li> <li>The user should be able to move through the cards by pressing the TAB or Arrow keys.</li> </ul> <p>Extra credit 🤓 -</p> <ul> <li>Mobile functionality. Users should be able to swipe right and left to move between cards</li> <li>The active card should always be entered horizontally within the container</li> <li>The library should be navigable without JavaScript enabled</li> </ul> <h2 id="my-solution" tabindex="-1">My Solution <a class="header-anchor" href="https://bitsofco.de/challenge-itunes-library/">#</a></h2> <h3 id="styling-the-cards" tabindex="-1">Styling the Cards <a class="header-anchor" href="https://bitsofco.de/challenge-itunes-library/">#</a></h3> <p>The first step was to style the cards so they looked like stacked items you could swipe through. This effect was achieved mainly though using a couple of transforms. First, I used the <code>skewY()</code> 2D transform, which skews the element along the Y-axis, giving it a distorted look. For example, this is an animation moving the value of the skew from <code>0</code> to <code>20deg</code> -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/xuevLy_O3b-738.avif 738w"><source type="image/gif" srcset="https://bitsofco.de/img/xuevLy_O3b-738.gif 738w"><img alt="Demo of skewY 2D transform" loading="lazy" decoding="async" src="https://bitsofco.de/img/xuevLy_O3b-738.webp" width="738" height="284"></picture></p> <p>I also used the <code>rotateY()</code> 3D transform, which rotates the element around it’s Y-axis, making it seem like it is folded towards the user. For example, this is an animation moving the value of the rotation from <code>0</code> to <code>50deg</code> -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/LTQesqfiLg-738.avif 738w"><source type="image/gif" srcset="https://bitsofco.de/img/LTQesqfiLg-738.gif 738w"><img alt="Demo of rotateY 3D transform" loading="lazy" decoding="async" src="https://bitsofco.de/img/LTQesqfiLg-738.webp" width="738" height="284"></picture></p> <p>I also added a few other styles, notably -</p> <ul> <li>Negative margins to have the appearance of the cards being stacked on top of each other</li> <li>Scale transform to shrink the card when not active</li> <li>Blur filter to create a blurred out effect</li> </ul> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.card</span> <span class="token punctuation">{</span><br><br> <span class="token property">z-index</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span><br> <span class="token property">flex-shrink</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span><br><br> <span class="token property">outline</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 150px<span class="token punctuation">;</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 150px<span class="token punctuation">;</span><br> <span class="token property">margin-left</span><span class="token punctuation">:</span> -40px<span class="token punctuation">;</span><br> <span class="token property">margin-right</span><span class="token punctuation">:</span> -40px<span class="token punctuation">;</span><br><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotateY</span><span class="token punctuation">(</span>60deg<span class="token punctuation">)</span> <span class="token function">skewY</span><span class="token punctuation">(</span>-5deg<span class="token punctuation">)</span> <span class="token function">scale</span><span class="token punctuation">(</span>0.8<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 1px 1px 5px 1px <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.3<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">blur</span><span class="token punctuation">(</span>2px<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token property">transition</span><span class="token punctuation">:</span> transform 1s<span class="token punctuation">,</span><br> margin-left 1s<span class="token punctuation">,</span><br> margin-right 1s<span class="token punctuation">,</span><br> box-shadow 1s<span class="token punctuation">,</span><br> filter 1s<span class="token punctuation">,</span><br> flex-shrink 1s<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h3 id="handling-focus-active-and-hover-states" tabindex="-1">Handling Focus, Active &amp; Hover States <a class="header-anchor" href="https://bitsofco.de/challenge-itunes-library/">#</a></h3> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/5xNztfP2CZ-770.avif 770w"><source type="image/gif" srcset="https://bitsofco.de/img/5xNztfP2CZ-770.gif 770w"><img alt="Hover and Focus State" loading="lazy" decoding="async" src="https://bitsofco.de/img/5xNztfP2CZ-770.webp" width="770" height="286"></picture></p> <p>In the markup, each &quot;card&quot; is simply a div, but it could be any element, interactive or non-interactive. However, I wanted to make sure the element would be focusable so the &quot;active&quot; card could be accessed using the keyboard tab or arrow keys. To ensure this, I used the <code>tabindex</code> attribute set to <code>0</code>, to add each card to the natural tabbing order of 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">&lt;</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>card<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><br> <span class="token comment">&lt;!-- card content here --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p>When the card is in focus or active, I apply the styles to make it pop out.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.card:active,<br>.card:focus,<br>.card.js-active</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br> <span class="token property">margin-left</span><span class="token punctuation">:</span> 40px<span class="token punctuation">;</span><br> <span class="token property">margin-right</span><span class="token punctuation">:</span> 40px<span class="token punctuation">;</span><br> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 2px 2px 10px 1px <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.3<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">filter</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br> <span class="token property">flex-shrink</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">z-index</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>When a user hovers over a card with their pointing device, I wanted to make it obvious that the card can be clicked to fully expand.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.card:hover:not(:active):not(:focus):not(.js-active)</span> <span class="token punctuation">{</span><br> <span class="token property">margin-left</span><span class="token punctuation">:</span> -20px<span class="token punctuation">;</span><br> <span class="token property">margin-right</span><span class="token punctuation">:</span> -20px<span class="token punctuation">;</span><br> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 2px 2px 10px 1px <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.3<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">filter</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br> <span class="token property">cursor</span><span class="token punctuation">:</span> pointer<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>To avoid conflict with the other styles, I made sure to only apply these mouse hover styles if the card is not currently active or in focus.</p> <h3 id="handling-swipes-and-keyboard-events" tabindex="-1">Handling Swipes and Keyboard Events <a class="header-anchor" href="https://bitsofco.de/challenge-itunes-library/">#</a></h3> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/i0xVbLqPAZ-356.avif 356w"><source type="image/gif" srcset="https://bitsofco.de/img/i0xVbLqPAZ-356.gif 356w"><img alt="Swiping through cards on mobile" loading="lazy" decoding="async" src="https://bitsofco.de/img/i0xVbLqPAZ-356.webp" width="356" height="696"></picture></p> <p>On mobile, I needed to make sure that users could use swiping gestures to move between individual cards. To do this, I used custom event listeners written by <a href="https://stackoverflow.com/a/17567696">cocco on Stack Overflow</a> -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Source - https://stackoverflow.com/a/17567696</span><br>window<span class="token punctuation">.</span><span class="token function-variable function">onload</span> <span class="token operator">=</span> <span class="token keyword">function</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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">d</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br> <span class="token keyword">var</span><br> <span class="token function-variable function">ce</span><span class="token operator">=</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">e<span class="token punctuation">,</span>n</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token keyword">var</span> a<span class="token operator">=</span>document<span class="token punctuation">.</span><span class="token function">createEvent</span><span class="token punctuation">(</span><span class="token string">"CustomEvent"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>a<span class="token punctuation">.</span><span class="token function">initCustomEvent</span><span class="token punctuation">(</span>n<span class="token punctuation">,</span><span class="token boolean">true</span><span class="token punctuation">,</span><span class="token boolean">true</span><span class="token punctuation">,</span>e<span class="token punctuation">.</span>target<span class="token punctuation">)</span><span class="token punctuation">;</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span><span class="token function">dispatchEvent</span><span class="token punctuation">(</span>a<span class="token punctuation">)</span><span class="token punctuation">;</span>a<span class="token operator">=</span><span class="token keyword">null</span><span class="token punctuation">;</span><span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br> nm<span class="token operator">=</span><span class="token boolean">true</span><span class="token punctuation">,</span>sp<span class="token operator">=</span><span class="token punctuation">{</span><span class="token literal-property property">x</span><span class="token operator">:</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token literal-property property">y</span><span class="token operator">:</span><span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">,</span>ep<span class="token operator">=</span><span class="token punctuation">{</span><span class="token literal-property property">x</span><span class="token operator">:</span><span class="token number">0</span><span class="token punctuation">,</span><span class="token literal-property property">y</span><span class="token operator">:</span><span class="token number">0</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br> touch<span class="token operator">=</span><span class="token punctuation">{</span><br> <span class="token function-variable function">touchstart</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>sp<span class="token operator">=</span><span class="token punctuation">{</span><span class="token literal-property property">x</span><span class="token operator">:</span>e<span class="token punctuation">.</span>touches<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>pageX<span class="token punctuation">,</span><span class="token literal-property property">y</span><span class="token operator">:</span>e<span class="token punctuation">.</span>touches<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>pageY<span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token function-variable function">touchmove</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>nm<span class="token operator">=</span><span class="token boolean">false</span><span class="token punctuation">;</span>ep<span class="token operator">=</span><span class="token punctuation">{</span><span class="token literal-property property">x</span><span class="token operator">:</span>e<span class="token punctuation">.</span>touches<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>pageX<span class="token punctuation">,</span><span class="token literal-property property">y</span><span class="token operator">:</span>e<span class="token punctuation">.</span>touches<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>pageY<span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token function-variable function">touchend</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><span class="token keyword">if</span><span class="token punctuation">(</span>nm<span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token function">ce</span><span class="token punctuation">(</span>e<span class="token punctuation">,</span><span class="token string">'fc'</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token keyword">else</span><span class="token punctuation">{</span><span class="token keyword">var</span> x<span class="token operator">=</span>ep<span class="token punctuation">.</span>x<span class="token operator">-</span>sp<span class="token punctuation">.</span>x<span class="token punctuation">,</span>xr<span class="token operator">=</span>Math<span class="token punctuation">.</span><span class="token function">abs</span><span class="token punctuation">(</span>x<span class="token punctuation">)</span><span class="token punctuation">,</span>y<span class="token operator">=</span>ep<span class="token punctuation">.</span>y<span class="token operator">-</span>sp<span class="token punctuation">.</span>y<span class="token punctuation">,</span>yr<span class="token operator">=</span>Math<span class="token punctuation">.</span><span class="token function">abs</span><span class="token punctuation">(</span>y<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">if</span><span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">max</span><span class="token punctuation">(</span>xr<span class="token punctuation">,</span>yr<span class="token punctuation">)</span><span class="token operator">></span><span class="token number">20</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token function">ce</span><span class="token punctuation">(</span>e<span class="token punctuation">,</span><span class="token punctuation">(</span>xr<span class="token operator">></span>yr<span class="token operator">?</span><span class="token punctuation">(</span>x<span class="token operator">&lt;</span><span class="token number">0</span><span class="token operator">?</span><span class="token string">'swipeLeft'</span><span class="token operator">:</span><span class="token string">'swipeRight'</span><span class="token punctuation">)</span><span class="token operator">:</span><span class="token punctuation">(</span>y<span class="token operator">&lt;</span><span class="token number">0</span><span class="token operator">?</span><span class="token string">'swipeUp'</span><span class="token operator">:</span><span class="token string">'swipeDown'</span><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><span class="token punctuation">;</span>nm<span class="token operator">=</span><span class="token boolean">true</span><span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token function-variable function">touchcancel</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>nm<span class="token operator">=</span><span class="token boolean">false</span><span class="token punctuation">}</span><br> <span class="token punctuation">}</span><span class="token punctuation">;</span><br> <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token keyword">var</span> a <span class="token keyword">in</span> touch<span class="token punctuation">)</span><span class="token punctuation">{</span>d<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span>a<span class="token punctuation">,</span>touch<span class="token punctuation">[</span>a<span class="token punctuation">]</span><span class="token punctuation">,</span><span class="token boolean">false</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>document<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre> <p>With these event listeners, I could detect if a user was swiping right (<code>swipeRight</code>) or left (<code>swipeLeft</code>). Depending on which direction, I could then set the active card to be the previous or next card in the group.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">swipeCard</span><span class="token punctuation">(</span><span class="token parameter">direction</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">let</span> focusedElement <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">'.card.js-active'</span><span class="token punctuation">)</span> <span class="token operator">?</span> activeCard <span class="token operator">:</span> document<span class="token punctuation">.</span>activeElement<span class="token punctuation">;</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span>focusedElement <span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span>focusedElement<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span><span class="token string">'card'</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span><br><br> <span class="token keyword">let</span> focusedElementIndex <span class="token operator">=</span> <span class="token number">0</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">&lt;</span> cards<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">if</span> <span class="token punctuation">(</span>cards<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token operator">===</span> focusedElement<span class="token punctuation">)</span> focusedElementIndex <span class="token operator">=</span> i<br> <span class="token punctuation">}</span><br><br> <span class="token keyword">switch</span><span class="token punctuation">(</span>direction<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">case</span> <span class="token string">'right'</span><span class="token operator">:</span><br> <span class="token function">setActiveCard</span><span class="token punctuation">(</span> cards<span class="token punctuation">[</span>focusedElementIndex <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">]</span> <span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">break</span><span class="token punctuation">;</span><br> <span class="token keyword">case</span> <span class="token string">'left'</span><span class="token operator">:</span><br> <span class="token function">setActiveCard</span><span class="token punctuation">(</span> cards<span class="token punctuation">[</span>focusedElementIndex <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">]</span> <span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">break</span><span class="token punctuation">;</span><br> <span class="token keyword">default</span><span class="token operator">:</span><br> <span class="token keyword">break</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br><span class="token punctuation">}</span><br><br>document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'swipeRight'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">swipeCard</span><span class="token punctuation">(</span><span class="token string">'right'</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>document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'swipeLeft'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">swipeCard</span><span class="token punctuation">(</span><span class="token string">'left'</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="centering-the-active-element" tabindex="-1">Centering the Active Element <a class="header-anchor" href="https://bitsofco.de/challenge-itunes-library/">#</a></h3> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/5xNztfP2CZ-770.avif 770w"><source type="image/gif" srcset="https://bitsofco.de/img/5xNztfP2CZ-770.gif 770w"><img alt="Active element is always centered within the container" loading="lazy" decoding="async" src="https://bitsofco.de/img/5xNztfP2CZ-770.webp" width="770" height="286"></picture></p> <p>Finally, I wanted to ensure that the active card will always be horizontally in the middle of the container element.</p> <p>In Flexbox, there is a property to ensure that a flex item will always be <em>vertically</em> in the middle of the container element, which is <code>align-self</code>. In the future, there will be a similar property for horizontal alignment, <code>justify-self</code> ( See the <a href="https://www.w3.org/TR/css-align-3/#justify-self-property">prospective specification</a> ). However, this is not yet available, so to do this, I had to use JavaScript.</p> <p>In the markup, there are two container elements - <code>.card-outer-container</code> and <code>.card-inner-container</code>.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>card-container-outer<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">&lt;</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>card-container-inner<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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 attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>card<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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 attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>card<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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 attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>card<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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 attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>card<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> ... <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p>The outer container is what defines the height and width of the group. The inner contain has a larger width, and is what I shift left and right to make sure the active card is aligned to the centre of the outer container.</p> <p>To get to the amount of pixels from the left I need to shift the inner container, I subtract the following values -</p> <ol> <li>Half the width of the outer container</li> <li>Half width width of the card element</li> <li>The offset of the currently active card from the left</li> </ol> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">alignActiveCard</span><span class="token punctuation">(</span><span class="token parameter">card</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">const</span> halfOuterContainerWidth <span class="token operator">=</span> outerContainer<span class="token punctuation">.</span>clientWidth <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> halfCardContainerWidth <span class="token operator">=</span> card<span class="token punctuation">.</span>clientWidth <span class="token operator">/</span> <span class="token number">2</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> cardOffset <span class="token operator">=</span> card<span class="token punctuation">.</span>offsetLeft<span class="token punctuation">;</span><br><br> <span class="token keyword">const</span> left <span class="token operator">=</span> halfOuterContainerWidth <span class="token operator">-</span> cardOffset <span class="token operator">-</span> halfCardContainerWidth<span class="token punctuation">;</span><br> innerContainer<span class="token punctuation">.</span>style<span class="token punctuation">.</span>transform <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">translateX(</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> left <span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">px)</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>That's it! You can view a full demo of this on <a href="https://github.com/ireade/ui-challenges/tree/gh-pages/2-itunes-library">GitHub</a>, and use it in a project yourself if you like.</p> <h2 id="other-solutions" tabindex="-1">Other Solutions <a class="header-anchor" href="https://bitsofco.de/challenge-itunes-library/">#</a></h2> <p>If you do this challenge, leave a comment below and I'll add a list of your solutions here.</p> Tools for Developing Accessible Websites 2016-11-01T00:00:00Z https://bitsofco.de/tools-for-developing-accessible-websites/ <p>Building websites that are accessible can be challenging for developers like myself that have never had to use any assistive technologies. Unlike visual issues such as layout which can be easily seen, accessibility issues can very easily go unnoticed if we don't have the correct tools to test for them.</p> <blockquote> <p>Accessibility doesn't have to be perfect, it just needs to be a little bit better than yesterday.</p> <p><cite><a href="https://twitter.com/ireaderinokun/status/784401867447078912">Leonie Watson at FronteersConf</a></cite></p> </blockquote> <p>There are a few tools I use regularly for this purpose that greatly help, so I thought I would share them. Because I do the vast majority of my development in Chrome, this list is biased to tools for Chrome in particular.</p> <h2 id="accessibility-developer-tools" tabindex="-1">Accessibility Developer Tools <a class="header-anchor" href="https://bitsofco.de/tools-for-developing-accessible-websites/">#</a></h2> <p><a href="https://chrome.google.com/webstore/detail/accessibility-developer-t/fpkknkljclfencbdbgkenhalefipecmb?hl=en">Accessibility Developer Tools</a> is an extension for Google Chrome created by the Google Accessibility team. The extension adds an extra panel in the developer tools drawer called &quot;Audits&quot;. In this panel, we can run Audits on the page related to Network Utilization, Web Page Performance, and of course Accessibility.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/TMFmZsPCH1-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/TMFmZsPCH1-780.webp 780w"><img alt="Screenshot of Panel" loading="lazy" decoding="async" src="https://bitsofco.de/img/TMFmZsPCH1-780.png" width="780" height="509"></picture></p> <p>The Accessibility Audit will test the web page against a pre-defined list of accessibility checks. It will list, in order of importance, any critical issues that need to be fixed, as well as the tests that were passed by the page.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/iZ4LiaXSNK-800.avif 800w"><source type="image/webp" srcset="https://bitsofco.de/img/iZ4LiaXSNK-800.webp 800w"><img alt="Accessibility Audit Results" loading="lazy" decoding="async" src="https://bitsofco.de/img/iZ4LiaXSNK-800.png" width="800" height="522"></picture></p> <p>In addition to the Accessibility Audit, we can inspect any particular element’s accessibility properties in the element inspector. There is a new panel added to the element inspector called &quot;Accessibility Properties&quot;, which will list any properties relevant to the particular element.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/GvUTUVrCNN-800.avif 800w"><source type="image/webp" srcset="https://bitsofco.de/img/GvUTUVrCNN-800.webp 800w"><img alt="Accessibility Properties Inspector" loading="lazy" decoding="async" src="https://bitsofco.de/img/GvUTUVrCNN-800.png" width="800" height="521"></picture></p> <h2 id="accessibility-inspector" tabindex="-1">Accessibility Inspector <a class="header-anchor" href="https://bitsofco.de/tools-for-developing-accessible-websites/">#</a></h2> <p>As part of an internal Chrome experiment, an <a href="https://docs.google.com/document/d/1bj9Dc3_DnezF-IeNg51LEG2zfGtxD3YKP5t7SBB_-Dk/edit">Accessibility Inspector</a> has been available in the Chrome Developer Tools (hidden <a href="https://gist.github.com/marcysutton/0a42f815878c159517a55e6652e3b23a">behind a flag</a>).</p> <p><img src="https://bitsofco.de/tools-for-developing-accessible-websites/" alt="Flag"></p> <p>The Accessibility Inspector is an extra panel in the element inspector, called &quot;Accessibility&quot;. This allows us to inspect particular elements on the page and receive information on it's accessibility properties. Unlike the Accessibility Developer Tools extension, this tool is able to offer us much more in-depth information about an element's accessibility properties due to greater access to the Accessibility API.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/OMkHoj3R58-800.avif 800w"><source type="image/webp" srcset="https://bitsofco.de/img/OMkHoj3R58-800.webp 800w"><img alt="Screenshot" loading="lazy" decoding="async" src="https://bitsofco.de/img/OMkHoj3R58-800.png" width="800" height="515"></picture></p> <h2 id="tenon" tabindex="-1">Tenon <a class="header-anchor" href="https://bitsofco.de/tools-for-developing-accessible-websites/">#</a></h2> <p><a href="https://tenon.io/">Tenon</a> is an extremely useful tool that can identify <a href="https://www.w3.org/TR/WCAG20/">WCAG 2.0</a> and <a href="https://www.section508.gov/">Section 508</a> issues in any environment, from local development to production. It is actually an API you pay to have access to, which can be integrated in your development workflow, giving in-depth accessibility analyses every step of the development.</p> <p>Additionally, there is also a free online tool that will produce an accessibility report on any web page or even snippet of code.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/fdDBaVXCLy-800.avif 800w"><source type="image/webp" srcset="https://bitsofco.de/img/fdDBaVXCLy-800.webp 800w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/fdDBaVXCLy-800.png" width="800" height="520"></picture></p> <h2 id="chrome-vox" tabindex="-1">Chrome Vox <a class="header-anchor" href="https://bitsofco.de/tools-for-developing-accessible-websites/">#</a></h2> <p>Ensuring a website works well on screen readers can be a bit of a guessing game for developers that don't already use screen readers. <a href="https://chrome.google.com/webstore/detail/chromevox/kgejglhpjiefppelpmljglcjbhoiplfn">Chrome Vox</a> is a simple and easy-to-use screen reader for Google Chrome that can be installed as an extension. Once enabled, it will allow you to navigate any web page through it.</p> <p>Here's a demo of me using it to navigate the homepage of this blog -</p> <p><a href="https://www.youtube.com/watch?v=N1c6CfUhdwo"><picture><source type="image/avif" srcset="https://bitsofco.de/img/zTov2kA7Pe-2880.avif 2880w"><source type="image/webp" srcset="https://bitsofco.de/img/zTov2kA7Pe-2880.webp 2880w"><img alt="Using Chrome Vox Screen Reader" loading="lazy" decoding="async" src="https://bitsofco.de/img/zTov2kA7Pe-2880.png" width="2880" height="1800"></picture></a></p> <p>Even though not all screen readers work in the same way, Chrome Vox is a simple one to get started and experience what it is like to use a screen reader.</p> <h2 id="high-contrast-extension" tabindex="-1">High Contrast (Extension) <a class="header-anchor" href="https://bitsofco.de/tools-for-developing-accessible-websites/">#</a></h2> <p><a href="https://chrome.google.com/webstore/detail/high-contrast/djcfdncoelnlbldjfhinnjlhdjlikmph?hl=en">High Contrast</a> is an extension for Google Chrome that allows you to change the colour schemes of any page to a high contrast one. Viewing a website through one of these filters can be helpful when making colour choices.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/CNg06-k0ym-1102.avif 1102w"><source type="image/gif" srcset="https://bitsofco.de/img/CNg06-k0ym-1102.gif 1102w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/CNg06-k0ym-1102.webp" width="1102" height="704"></picture></p> <h2 id="your-keyboard" tabindex="-1">Your Keyboard <a class="header-anchor" href="https://bitsofco.de/tools-for-developing-accessible-websites/">#</a></h2> <p>Finally, one of the simplest and most useful ways to test a website is to try navigating it with only a keyboard, no pointing device.</p> Push Notifications on the Web (Building a PWA, Part 3) 2016-10-25T00:00:00Z https://bitsofco.de/bitsofcode-pwa-part-3-push-notifications/ <p>Last week's article was Part 2 (of 3) in my series on how I built a Progressive Web Application for this blog.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/ByMSIoeIJt-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/ByMSIoeIJt-780.webp 780w"><img alt="The application" loading="lazy" decoding="async" src="https://bitsofco.de/img/ByMSIoeIJt-780.png" width="780" height="453"></picture></p> <p><a href="https://app.bitsofco.de">Get the Application</a> | <a href="https://github.com/ireade/app.bitsofco.de">View Source</a></p> <p>In this final part, I will show how I implemented web push notifications. For users that subscribe, a notification is sent to their device once a week when a new article is released on the blog.</p> <blockquote> <p><a href="https://twitter.com/hashtag/ProgressiveWebApp?src=hash">#ProgressiveWebApp</a> works, rocks! Here is a notification I got from the bitsofcode <a href="https://twitter.com/hashtag/PWA?src=hash">#PWA</a> by <a href="https://twitter.com/ireaderinokun">@ireaderinokun</a> <a href="https://twitter.com/hashtag/devfest16?src=hash">#devfest16</a></p> <p><img src="https://pbs.twimg.com/media/CvEMov_WIAA3yfp.jpg" alt=""> — Odili Charles Opute (@chaluwa) <a href="https://twitter.com/chaluwa/status/788425374589972481">October 18, 2016</a></p> </blockquote> <p>Web Applications can use the <a href="https://developer.mozilla.org/en/docs/Web/API/Push_API">Push API</a>, in combination with the Service Worker, to receive push notifications messages pushed to them from a server, whether or not the web app is in the foreground, or even currently loaded, on a user agent. It works in the following way -</p> <ul> <li>The user subscribes to receive push notifications via the Service Worker's Push Manager</li> <li>We receive and endpoint with the user's subscription ID, which we save securely in a database</li> <li>We pass the user's subscription ID when we post messages from the server to our messaging service (in this case, <a href="https://firebase.google.com/docs/cloud-messaging/">Firebase Cloud Messaging</a>)</li> </ul> <p>So there are two parts to this. First, the client side, where we let the user subscribe to push notifications and display the notifications sent to them. Second, the server side, where we store the subscription IDs securely, and post the messages to the messaging service. This second part was done with the help of <a href="https://twitter.com/timigod">Timi Ajiboye</a>, who created a simple Rails application for this purpose. If you're interested in seeing how he did that, you can read <a href="https://chunksofco.de/push-notifications-on-the-web-building-a-pwa-crossover-20f0317987de">his article here</a>. In this article I will cover the first part, the client side.</p> <h2 id="setting-up-firebase-cloud-messaging" tabindex="-1">Setting up Firebase Cloud Messaging <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-3-push-notifications/">#</a></h2> <p>The first thing we need to do is <a href="https://console.firebase.google.com">setup a new project with Firebase</a> and get our Firebase Cloud Messaging <strong>Sender ID</strong> and <strong>Server Key</strong>. These can be found under <strong>Settings &gt; Cloud Messaging</strong> in the Firebase project.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/JypA72kNcO-950.avif 950w"><source type="image/webp" srcset="https://bitsofco.de/img/JypA72kNcO-950.webp 950w"><img alt="Sender ID and Server Key in Firebase Cloud Messaging" loading="lazy" decoding="async" src="https://bitsofco.de/img/JypA72kNcO-950.png" width="950" height="659"></picture></p> <p>The Sender ID goes in our application's <code>manifest.json</code> file, and is used to identify our client.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token punctuation">{</span><br> <span class="token string-property property">"gcm_sender_id"</span><span class="token operator">:</span> <span class="token string">"FIREBASE_CLOUD_MESSAGING_SENDER_ID_HERE"</span><span class="token punctuation">,</span><br> <span class="token comment">// more manifest.json stuff...</span><br><span class="token punctuation">}</span></code></pre> <p>We use the Server Key when posting to Firebase Cloud Messaging via our server (see the <a href="https://chunksofco.de/push-notifications-on-the-web-building-a-pwa-crossover-20f0317987de">tutorial for the rails application</a>).</p> <h2 id="subscribing-to-push-notifications" tabindex="-1">Subscribing to Push Notifications <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-3-push-notifications/">#</a></h2> <p>To use the Push API, we need to have a registered and active Service Worker, as the Push Manager service is only available within the Service Worker registration.</p> <pre class="language-js" tabindex="0"><code class="language-js">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><br> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">reg</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>reg<span class="token punctuation">)</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Lh9VxjYUi--631.avif 631w"><source type="image/webp" srcset="https://bitsofco.de/img/Lh9VxjYUi--631.webp 631w"><img alt="Push Manager in Service Worker Registration" loading="lazy" decoding="async" src="https://bitsofco.de/img/Lh9VxjYUi--631.png" width="631" height="211"></picture></p> <p>To subscribe a user to push notifications, all we need to do is call the <code>subscribe()</code> method of the push manager. This will generate a popup in the browser, asking the user if they will allow push notifications from the domain. We pass the <code>subscribe()</code> method an object with <code>userVisibleOnly: true</code> to indicate that the push subscription will only be used for messages whose effect is made visible to the user.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">subscribe</span><span class="token punctuation">(</span><span class="token parameter">serviceWorkerReg</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> serviceWorkerReg<span class="token punctuation">.</span>pushManager<span class="token punctuation">.</span><span class="token function">subscribe</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">userVisibleOnly</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">}</span><span class="token punctuation">)</span><br><span class="token punctuation">}</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><br> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">reg</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">subscribe</span><span class="token punctuation">(</span>reg<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/WzNYAIIucs-2026.avif 2026w"><source type="image/webp" srcset="https://bitsofco.de/img/WzNYAIIucs-2026.webp 2026w"><img alt="Push Notification Popup in Browser" loading="lazy" decoding="async" src="https://bitsofco.de/img/WzNYAIIucs-2026.png" width="2026" height="1196"></picture></p> <p>Although I've set it to automatically ask the user to subscribe once the Service Worker is registered in this example, this isn't the most user friendly method. Only prompt the user to subscribe to push notifications when it makes sense to do so.</p> <p>Once the user has subscribed, we need to pass their subscription ID to our server so that it can be saved securely in the database. We do this using the API endpoint provided by the <a href="https://chunksofco.de/push-notifications-on-the-web-building-a-pwa-crossover-20f0317987de">rails application</a>.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">addSubscriptionIDToDB</span><span class="token punctuation">(</span><span class="token parameter">subscription</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> uid <span class="token operator">=</span> subscription<span class="token punctuation">.</span>endpoint<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">'gcm/send/'</span><span class="token punctuation">)</span><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> 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 keyword">new</span> <span class="token class-name">Headers</span><span class="token punctuation">(</span><span class="token punctuation">{</span><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><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> <span class="token literal-property property">uid</span><span class="token operator">:</span> uid <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 function">fetch</span><span class="token punctuation">(</span>apiUrl<span class="token punctuation">,</span> options<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">subscribe</span><span class="token punctuation">(</span><span class="token parameter">serviceWorkerReg</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> serviceWorkerReg<span class="token punctuation">.</span>pushManager<span class="token punctuation">.</span><span class="token function">subscribe</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token literal-property property">userVisibleOnly</span><span class="token operator">:</span> <span class="token boolean">true</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">subscription</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">addSubscriptionIDToDB</span><span class="token punctuation">(</span>subscription<span class="token punctuation">)</span><span class="token punctuation">)</span><br><span class="token punctuation">}</span></code></pre> <p>Now the database has all the information it needs to send the notifications.</p> <h2 id="unsubscribing-from-push-notifications" tabindex="-1">Unsubscribing from Push Notifications <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-3-push-notifications/">#</a></h2> <p>We also need to give the user the ability to unsubscribe from receiving push notifications. Like when we subscribed, we need to do two things - unsubscribe from the Push Manager and delete the subscription ID from the database.</p> <p>To unsubscribe from the push manager, we need to get the user's current subscription. We can do this using the <code>getSubscription()</code> method. Once we have the user's current subscription, we can unsubscribe them with the <code>unsubscribe()</code> method.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">unsubscribe</span><span class="token punctuation">(</span><span class="token parameter">serviceWorkerReg</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> serviceWorkerReg<span class="token punctuation">.</span>pushManager<span class="token punctuation">.</span><span class="token function">getSubscription</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">subscription</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> subscription<span class="token punctuation">.</span><span class="token function">unsubscribe</span><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><br><span class="token punctuation">}</span></code></pre> <p>Next, we need to remove the user's subscription ID from our database. Again, we use the API endpoint provided by the <a href="https://chunksofco.de/push-notifications-on-the-web-building-a-pwa-crossover-20f0317987de">rails application</a>.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">deleteSubscriptionIDFromDB</span><span class="token punctuation">(</span><span class="token parameter">subscription</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> uid <span class="token operator">=</span> subscription<span class="token punctuation">.</span>endpoint<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">'gcm/send/'</span><span class="token punctuation">)</span><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> 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">'DELETE'</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><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><span class="token punctuation">)</span><br> <span class="token punctuation">}</span><span class="token punctuation">;</span><br> <span class="token function">fetch</span><span class="token punctuation">(</span>apiUrl<span class="token operator">+</span>uid<span class="token punctuation">,</span> options<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">unsubscribe</span><span class="token punctuation">(</span><span class="token parameter">serviceWorkerReg</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> serviceWorkerReg<span class="token punctuation">.</span>pushManager<span class="token punctuation">.</span><span class="token function">getSubscription</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">subscription</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> subscription<span class="token punctuation">.</span><span class="token function">unsubscribe</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 punctuation">)</span> <span class="token operator">=></span> <span class="token function">deleteSubscriptionIDFromDB</span><span class="token punctuation">(</span>subscription<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></code></pre> <h2 id="sending-notifications-on-a-weekly-schedule" tabindex="-1">Sending Notifications on a Weekly Schedule <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-3-push-notifications/">#</a></h2> <p>Finally, we want to trigger the sending of notifications. The <a href="https://chunksofco.de/push-notifications-on-the-web-building-a-pwa-crossover-20f0317987de">rails application</a> has an endpoint which we can POST to, to send a notification.</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">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 keyword">new</span> <span class="token class-name">Headers</span><span class="token punctuation">(</span><span class="token punctuation">{</span><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><span class="token punctuation">)</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span><br><span class="token function">fetch</span><span class="token punctuation">(</span>apiUrl<span class="token punctuation">,</span> options<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>I could run this script manually everytime I wanted to send a notification to all users, but that won't be very efficient. So, I decided to setup an <a href="https://ifttt.com">IFTTT</a> recipe to make this POST request every week at 10am BST (to coincide with the newsletter going out).</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/BcxM3r7nwF-1464.avif 1464w"><source type="image/webp" srcset="https://bitsofco.de/img/BcxM3r7nwF-1464.webp 1464w"><img alt="IFTT Recipe - If every day of the week at 12:45 PM on Tue, then make a web request" loading="lazy" decoding="async" src="https://bitsofco.de/img/BcxM3r7nwF-1464.png" width="1464" height="512"></picture></p> <h2 id="support-for-push-api" tabindex="-1">Support for Push API <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-3-push-notifications/">#</a></h2> <p><a href="https://caniuse.com/#feat=push-api">Can I Use push-api?</a> Data on support for the push-api feature across the major browsers from caniuse.com.</p> "Instant Loading" with IndexedDB (Building a PWA, Part 2) 2016-10-18T00:00:00Z https://bitsofco.de/bitsofcode-pwa-part-2-instant-loading-with-indexeddb/ <p>Last week's article was Part 1 (of 3) in my series on how I built a Progressive Web Application for this blog.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/ByMSIoeIJt-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/ByMSIoeIJt-780.webp 780w"><img alt="The application" loading="lazy" decoding="async" src="https://bitsofco.de/img/ByMSIoeIJt-780.png" width="780" height="453"></picture></p> <p><a href="https://app.bitsofco.de">Get the Application</a> | <a href="https://github.com/ireade/app.bitsofco.de">View Source</a></p> <p>As I mentioned, there are two parts to creating an &quot;offline-first&quot; application -</p> <ol> <li>Cache the app shell so pages and assets are accessible offline</li> <li>Load locally-saved data first, then fetch updated data later if the network permits</li> </ol> <p>Last week, I showed how I achieved the first using Service Worker to cache the app shell and serve it to the user even when they are offline. In this article, I will show how I used IndexedDB to save the fetched data for offline use. In the final part of this series next week, I will demonstrate how I implemented push notifications.</p> <h2 id="what-is-indexeddb" tabindex="-1">What is IndexedDB? <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-2-instant-loading-with-indexeddb/">#</a></h2> <p>IndexedDB is a &quot;low-level API for client-side storage of significant amounts of structured data&quot; (<a href="https://developer.mozilla.org/en/docs/Web/API/IndexedDB_API">Mozilla</a>). It is a JavaScript-based, object-oriented, database that allows us to easily store and retrieve data that has been indexed with a key. IndexedDB can take some getting used to as it very low-level and works quite differently to other databases.</p> <p>For the bitsofcode web app, I used IndexedDB to store the articles fetched from the blog RSS feed. This way, articles could be loaded instantly from the database first, before checking over the network for the more up-to-date data.</p> <h2 id="creating-a-database-and-store" tabindex="-1">Creating a Database and Store <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-2-instant-loading-with-indexeddb/">#</a></h2> <p>For my own sanity (and because I love <a href="https://bitsofco.de/javascript-promises-101">Promises</a>), I use Jake Archibald’s <a href="https://github.com/jakearchibald/idb">IndexedDB Promised library</a> which offers a Promise-ified version of the IndexedDB methods. All my examples will be using this library.</p> <p>To start using IndexedDB, we need to create our database and the stores (which are like tables) within the database. To do that, we need to <code>open</code> up IndexedDB with the database name and version.</p> <pre class="language-js" tabindex="0"><code class="language-js">idb<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">'DATABASE_NAME'</span><span class="token punctuation">,</span> <span class="token constant">VERSION</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">upgradeDb</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Create stores</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>To create stores in our database, we use the <code>createObjectStore</code> method of <code>upgradeDb</code>. When creating a store, we pass two arguments -</p> <ol> <li>The name of the store</li> <li>An object with some optional settings. For example, the <code>keyPath</code> property, which allows us to specify what key in the object we want to be the primary key.</li> </ol> <p>Here it is all together -</p> <pre class="language-js" tabindex="0"><code class="language-js">idb<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">'DATABASE_NAME'</span><span class="token punctuation">,</span> <span class="token constant">VERSION</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">upgradeDb</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> store <span class="token operator">=</span> upgradeDb<span class="token punctuation">.</span><span class="token function">createObjectStore</span><span class="token punctuation">(</span><span class="token string">'STORE_NAME'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">keyPath</span><span class="token operator">:</span> <span class="token string">'PRIMARY_KEY_NAME'</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>We can also define indexes for the stores. Indexes provide us alternative ways of accessing the data. For example, in a store we may have data that has, amongst others, a <code>timestamp</code> and <code>name</code> key. If we ever want to retrieve the data by timestamp or name, we may want to have indexes for each of these keys.</p> <pre class="language-js" tabindex="0"><code class="language-js">idb<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">'DATABASE_NAME'</span><span class="token punctuation">,</span> <span class="token constant">VERSION</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">upgradeDb</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> store <span class="token operator">=</span> upgradeDb<span class="token punctuation">.</span><span class="token function">createObjectStore</span><span class="token punctuation">(</span><span class="token string">'STORE_NAME'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">keyPath</span><span class="token operator">:</span> <span class="token string">'PRIMARY_KEY_NAME'</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> store<span class="token punctuation">.</span><span class="token function">createIndex</span><span class="token punctuation">(</span><span class="token string">'INDEX_NAME'</span><span class="token punctuation">,</span> <span class="token string">'KEY_PATH'</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>For the bitsofcode application, I created 3 stores - an <code>ArticlesStore</code>, a <code>BookmarksStore</code> and a <code>SettingsStore</code>. Here is how I created the first.</p> <pre class="language-js" tabindex="0"><code class="language-js">idb<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">'bitsofcode'</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">upgradeDb</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> ArticlesStore <span class="token operator">=</span> upgradeDb<span class="token punctuation">.</span><span class="token function">createObjectStore</span><span class="token punctuation">(</span><span class="token string">'Articles'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">keyPath</span><span class="token operator">:</span> <span class="token string">'guid'</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> ArticlesStore<span class="token punctuation">.</span><span class="token function">createIndex</span><span class="token punctuation">(</span><span class="token string">'guid'</span><span class="token punctuation">,</span> <span class="token string">'guid'</span><span class="token punctuation">)</span><br><br> <span class="token comment">// Other stores..</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>This process of opening up IndexedDB and creating/updating the database itself returns the database, to which we can create/read/update/delete data. Because of this, it is best to wrap the creation of the database and stores in a function that can be reused.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">OpenIDB</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> idb<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">'bitsofcode'</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">upgradeDb</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> ArticlesStore <span class="token operator">=</span> upgradeDb<span class="token punctuation">.</span><span class="token function">createObjectStore</span><span class="token punctuation">(</span><span class="token string">'Articles'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">keyPath</span><span class="token operator">:</span> <span class="token string">'guid'</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token comment">// etc etc</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></code></pre> <h2 id="data-crud" tabindex="-1">Data CRUD <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-2-instant-loading-with-indexeddb/">#</a></h2> <p>Once the database and stores have been created, we can move on the the CRUD actions.</p> <h3 id="create-update" tabindex="-1">Create / Update <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-2-instant-loading-with-indexeddb/">#</a></h3> <p>IndexedDB is a transactional database system, which is a system where operations on the database can be rolled back if not completed properly. This means that whenever we want to interact with any of our data, we need to first open up a new transaction, perform our actions, then either commit or rollback the transaction depending on if things succeed. This may seem like a lot of tedious work to do, but this is critical for maintaining the integrity of data.</p> <p>Therefore, to create (or update) data in our IndexedDB, we need to do the following things -</p> <ol> <li>Open up the database</li> <li>Open a new read/write transaction with the store within the database</li> <li>Add the data to the store</li> <li>Complete the transaction</li> </ol> <p>These steps looks like this -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// 1. Open up the database</span><br><span class="token function">OpenIDB</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">db</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> dbStore <span class="token operator">=</span> <span class="token string">'ArticlesStore'</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 2. Open a new read/write transaction with the store within the database</span><br> <span class="token keyword">const</span> transaction <span class="token operator">=</span> db<span class="token punctuation">.</span><span class="token function">transaction</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">,</span> <span class="token string">'readwrite'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> store <span class="token operator">=</span> transaction<span class="token punctuation">.</span><span class="token function">objectStore</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 3. Add the data to the store</span><br> store<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 4. Complete the transaction</span><br> <span class="token keyword">return</span> transaction<span class="token punctuation">.</span>complete<span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p>We are using the <code>store.put()</code> method here, which is also used for updating data in the database.</p> <h3 id="read" tabindex="-1">Read <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-2-instant-loading-with-indexeddb/">#</a></h3> <p>To retrieve data from IndexedDB, we need to do the following things -</p> <ol> <li>Open up the database</li> <li>Open a new read-only transaction with the store within the database</li> <li>Return the data</li> </ol> <p>These steps looks like this -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// 1. Open up the database</span><br><span class="token function">OpenIDB</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">db</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> dbStore <span class="token operator">=</span> <span class="token string">'ArticlesStore'</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 2. Open a new read-only transaction with the store within the database</span><br> <span class="token keyword">const</span> transaction <span class="token operator">=</span> db<span class="token punctuation">.</span><span class="token function">transaction</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> store <span class="token operator">=</span> transaction<span class="token punctuation">.</span><span class="token function">objectStore</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 3. Return the data</span><br> <span class="token keyword">return</span> store<span class="token punctuation">.</span><span class="token function">getAll</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></code></pre> <p>We can also get data by a specific index -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// 1. Open up the database</span><br><span class="token function">OpenIDB</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">db</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> dbStore <span class="token operator">=</span> <span class="token string">'ArticlesStore'</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 2. Open a new read-only transaction with the store within the database</span><br> <span class="token keyword">const</span> transaction <span class="token operator">=</span> db<span class="token punctuation">.</span><span class="token function">transaction</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> store <span class="token operator">=</span> transaction<span class="token punctuation">.</span><span class="token function">objectStore</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">const</span> index <span class="token operator">=</span> store<span class="token punctuation">.</span><span class="token function">index</span><span class="token punctuation">(</span><span class="token string">'INDEX_NAME'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 3. Return the data</span><br> <span class="token keyword">return</span> index<span class="token punctuation">.</span><span class="token function">getAll</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></code></pre> <p>We can even get data by a value that correlates to the index -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// 1. Open up the database</span><br><span class="token function">OpenIDB</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">db</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> dbStore <span class="token operator">=</span> <span class="token string">'ArticlesStore'</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 2. Open a new read-only transaction with the store within the database</span><br> <span class="token keyword">const</span> transaction <span class="token operator">=</span> db<span class="token punctuation">.</span><span class="token function">transaction</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> store <span class="token operator">=</span> transaction<span class="token punctuation">.</span><span class="token function">objectStore</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">const</span> index <span class="token operator">=</span> store<span class="token punctuation">.</span><span class="token function">index</span><span class="token punctuation">(</span><span class="token string">'INDEX_NAME'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 3. Return the data</span><br> <span class="token keyword">return</span> index<span class="token punctuation">.</span><span class="token function">getAll</span><span class="token punctuation">(</span><span class="token string">'VALUE'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <h3 id="delete" tabindex="-1">Delete <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-2-instant-loading-with-indexeddb/">#</a></h3> <p>Finally, to delete data from IndexedDB, we need to do the following things -</p> <ol> <li>Open up the database</li> <li>Open a new read/write transaction with the store within the database</li> <li>Delete the data corresponding to the passed key</li> <li>Complete the transaction</li> </ol> <p>These steps looks like this -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// 1. Open up the database</span><br><span class="token function">OpenIDB</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">db</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> dbStore <span class="token operator">=</span> <span class="token string">'ArticlesStore'</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 2. Open a new read/write transaction with the store within the database</span><br> <span class="token keyword">const</span> transaction <span class="token operator">=</span> db<span class="token punctuation">.</span><span class="token function">transaction</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">,</span> <span class="token string">'readwrite'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> store <span class="token operator">=</span> transaction<span class="token punctuation">.</span><span class="token function">objectStore</span><span class="token punctuation">(</span>dbStore<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 3. Delete the data corresponding to the passed key</span><br> store<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span><span class="token string">'DATA_KEY'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// 4. Complete the transaction</span><br> <span class="token keyword">return</span> transaction<span class="token punctuation">.</span>complete<span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <h2 id="instant-loading-data-with-indexeddb" tabindex="-1">Instant Loading Data with IndexedDB <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-2-instant-loading-with-indexeddb/">#</a></h2> <p>By saving the articles locally in IndexedDB, I was able to first serve those locally saved articles to users first, before going to the network to get the most up-to-date data. This is what the sequence of functions look like for the Latest Articles page.</p> <pre class="language-js" tabindex="0"><code class="language-js">Database<span class="token punctuation">.</span><span class="token function">retrieve</span><span class="token punctuation">(</span><span class="token string">'Articles'</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">articlesFromDatabase</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>articlesFromDatabase<span class="token punctuation">.</span>length <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token function">fetchArticles</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><br> didFetchArticlesFromDatabase <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span>articlesFromDatabase<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 function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">articles</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> Articles <span class="token operator">=</span> <span class="token function">sortedArticles</span><span class="token punctuation">(</span>articles<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token function">displayArticles</span><span class="token punctuation">(</span>Articles<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</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><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> <span class="token keyword">if</span> <span class="token punctuation">(</span>didFetchArticlesFromDatabase<span class="token punctuation">)</span> <span class="token function">updateArticlesInBackground</span><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 creates an &quot;Instant Loading&quot; experience for users, with page load happening in <strong>144ms</strong>!</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/gMxUB4q8M9-2434.avif 2434w"><source type="image/webp" srcset="https://bitsofco.de/img/gMxUB4q8M9-2434.webp 2434w"><img alt="Chrome developer tools showing page load in 144ms" loading="lazy" decoding="async" src="https://bitsofco.de/img/gMxUB4q8M9-2434.png" width="2434" height="1558"></picture></p> <h2 id="support-for-indexeddb" tabindex="-1">Support for IndexedDB <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-2-instant-loading-with-indexeddb/">#</a></h2> <p><a href="https://caniuse.com/#feat=indexeddb">Can I Use indexeddb?</a> Data on support for the indexeddb feature across the major browsers from caniuse.com.</p> “Offline First” with Service Worker (Building a PWA, Part 1) 2016-10-11T00:00:00Z https://bitsofco.de/bitsofcode-pwa-part-1-offline-first-with-service-worker/ <p>Over the past 5 months, I have been doing Udacity’s <a href="https://www.udacity.com/course/senior-web-developer-nanodegree--nd802">Senior Web Developer Nanodegree</a>. For my final Capstone project (I've graduated now! 💃🏿), I had to create my very own web application that would function just as a native mobile application would, using all I learned throughout the Nanodegree. So, I decided to create a “Progressive Web Application” for this blog.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/ByMSIoeIJt-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/ByMSIoeIJt-780.webp 780w"><img alt="The application" loading="lazy" decoding="async" src="https://bitsofco.de/img/ByMSIoeIJt-780.png" width="780" height="453"></picture></p> <p><a href="https://app.bitsofco.de">Get the Application</a> | <a href="https://github.com/ireade/app.bitsofco.de">View Source</a></p> <h2 id="the-application" tabindex="-1">The Application <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-1-offline-first-with-service-worker/">#</a></h2> <p>The application I created allows users to read the latest articles from the blog (even when offline), bookmark interesting articles, and receive push notifications when a new article is released.</p> <p>There are four pages available -</p> <ul> <li>The <strong>Homepage</strong> is just an introduction to the application. Here is also the setting to enable/disable push notifications when a new article is released.</li> <li>On <strong>Latest Articles</strong> page are the latest articles from the RSS feed. It displays excerpts from the latest 10-15 articles from the blog.</li> <li>The <strong>Bookmarks</strong> page is the list of bookmarked articles. It displays the excerpts of all bookmarked articles.</li> <li>Finally, the <strong>Single Article</strong> page displays the full article the user is reading.</li> </ul> <p>The primary feature of the application is it's offline capabilities. There are two parts to creating an &quot;offline-first&quot; application -</p> <ol> <li><strong>Cache the app shell</strong> so pages and assets are accessible offline</li> <li><strong>Load locally-saved data first</strong>, then fetch updated data later if the network permits</li> </ol> <p>In this article, I will go over how I achieve the first, using Service Worker. Next week, I will go over the second part. In the final part of this series, I will demonstrate how I implemented push notifications.</p> <h3 id="libraries-and-tools" tabindex="-1">Libraries &amp; Tools <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-1-offline-first-with-service-worker/">#</a></h3> <p>To make it as light as possible, I decided to build this application without the use of any front-end framework, just plain 'ol JavaScript. Here are some of the tools I used -</p> <ul> <li><a href="https://gulpjs.com/">Gulp.js</a> for build process</li> <li><a href="https://handlebarsjs.com/">Handlebars.js</a> for templating</li> <li><a href="https://sass-lang.com/">SASS</a> for awesome CSS</li> <li><a href="https://babeljs.io/">Babel</a> for compiling ES2015</li> <li><a href="https://firebase.google.com">Firebase</a> for hosting &amp; SSL</li> </ul> <h2 id="the-app-shell-model" tabindex="-1">The App Shell Model <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-1-offline-first-with-service-worker/">#</a></h2> <p>The App Shell Model is a way of architecting a progressive web application. The app shell is the &quot;minimal HTML, CSS and JavaScript powering a user interface&quot; (<a href="https://developers.google.com/web/updates/2015/11/app-shell?hl=en">Google Developers</a>). It is all the parts of the page that are static and can be cached, so that it only needs to be loaded once.</p> <p>For example on the Latest Articles page, this is the shell (left) and when the articles are fetched, they are injected into the main part of the page (right).</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/GGfdl_JeVf-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/GGfdl_JeVf-780.webp 780w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/GGfdl_JeVf-780.png" width="780" height="464"></picture></p> <p>On first visit over a 3G connection, the page will load at a relatively normal pace. This is when the app shell is cached. On repeated visits, the page shell loads almost instantly because there is no need to fetch it from a remote source.</p> <h2 id="precaching-the-app-shell" tabindex="-1">Precaching the App Shell <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-1-offline-first-with-service-worker/">#</a></h2> <p>The cornerstone of a Progressive Web Application is the Service Worker. The Service Worker allows us an unprecedented control over the network by allowing us to intercept any and all fetch requests from the main document. Combined with the Cache Storage API, we can use the Service Worker to quickly serve content, even while completely offline.</p> <p>In my <a href="https://bitsofco.de/setting-up-a-basic-service-worker">Setting up a Basic Service Worker</a> video tutorial, I showed how to handle precaching the app shell completely manually. In that method, we had to handle adding files to a versioned cache as well as checking for files from previous cache versions and clearing them out, like so -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> cacheName <span class="token operator">=</span> <span class="token string">'v1'</span><span class="token punctuation">;</span><br><span class="token keyword">var</span> cacheFiles <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token string">'./'</span><span class="token punctuation">,</span><br> <span class="token string">'./index.html'</span><span class="token punctuation">,</span><br> <span class="token string">'./js/app.js'</span><span class="token punctuation">,</span><br> <span class="token string">'./css/reset.css'</span><span class="token punctuation">,</span><br> <span class="token string">'./css/style.css'</span><br><span class="token punctuation">]</span><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">'install'</span><span class="token punctuation">,</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> e<span class="token punctuation">.</span><span class="token function">waitUntil</span><span class="token punctuation">(</span><br> <span class="token comment">// Open the cache</span><br> caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span>cacheName<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">cache</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Add all the default files to the cache</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'[ServiceWorker] Caching cacheFiles'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> cache<span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>cacheFiles<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><span class="token punctuation">}</span><span class="token punctuation">)</span><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">'activate'</span><span class="token punctuation">,</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> e<span class="token punctuation">.</span><span class="token function">waitUntil</span><span class="token punctuation">(</span><br> <span class="token comment">// Get all the cache keys (cacheName)</span><br> caches<span class="token punctuation">.</span><span class="token function">keys</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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">cacheNames</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span>cacheNames<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">thisCacheName</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// If a cached item is saved under a previous cacheName</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>thisCacheName <span class="token operator">!==</span> cacheName<span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Delete that cached file</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'[ServiceWorker] Removing Cached Files from Cache - '</span><span class="token punctuation">,</span> thisCacheName<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> caches<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span>thisCacheName<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><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><span class="token punctuation">;</span></code></pre> <p>However, there is now a much easier way to handle precache assets, with Google’s <a href="https://github.com/GoogleChrome/sw-toolbox">Service Worker Toolbox</a> library*. Now, to add assets for precache, all we have to do is pass the toolbox an array of the files, and everything else will be handled for us.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Import the Service Worker Toolbox file</span><br><span class="token function">importScripts</span><span class="token punctuation">(</span><span class="token string">'js/lib/sw-toolbox/sw-toolbox.js'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> precacheFiles <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token string">'./'</span><span class="token punctuation">,</span><br> <span class="token string">'./index.html'</span><span class="token punctuation">,</span><br> <span class="token string">'./js/app.js'</span><span class="token punctuation">,</span><br> <span class="token string">'./css/reset.css'</span><span class="token punctuation">,</span><br> <span class="token string">'./css/style.css'</span><br><span class="token punctuation">]</span><span class="token punctuation">;</span><br><br><span class="token comment">// Precache the files</span><br>toolbox<span class="token punctuation">.</span><span class="token function">precache</span><span class="token punctuation">(</span>precacheFiles<span class="token punctuation">)</span></code></pre> <p>We can check that the files have been cached in our developer tools. In Chrome, we can find the Cache Storage under <strong>Developer Tools &gt; Application &gt; Cache Storage</strong>. There should be a new store titled something like <code>$$$toolbox-cache$$$SITE_URL_HERE$$$</code>. For example, the storage for the bitsofcode app is called <code>$$$toolbox-cache$$$https://app.bitsofco.de/$$$</code>. When we click on the cache store, we can see the list of files we have cached. <small>* Note - In a recent update, Google created a separate library, [Service Worker Preacache](https://github.com/GoogleChrome/sw-precache), which deals with precaching of assets alone. It works in a totally different way to the toolbox. It works with your build tool, e.g. Gulp, and generates the Service Worker file for you. Because of this major difference, I decided to stick with the toolbox as it is what I used when I did my Nanodegree. However, I might write about the new Precache library in a later article.</small></p> <h2 id="serving-the-precached-files" tabindex="-1">Serving the Precached Files <a class="header-anchor" href="https://bitsofco.de/bitsofcode-pwa-part-1-offline-first-with-service-worker/">#</a></h2> <p>Getting the files into the cache is only half the battle, we need to actually serve it to the user. In our Service Worker, we can listen for when there is a fetch request from the document using the <code>fetch</code> event listener.</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">event</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></code></pre> <p>Within this event, what we want to do it this -</p> <ol> <li>Check the cache if a file matching that request is available</li> <li>If it is, respond to the document with the file from the cache</li> <li>If it isn’t, fetch the file from the network and respond to the document with the fetched file</li> </ol> <p>Here is how we can execute that -</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">event</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// Respond to the document with what is returned from </span>
<br> event<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span><br><br> <span class="token comment">// 1. Check the cache if a file matching that request is available</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><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><br> <span class="token comment">// 2. If it is, respond to the document with the file from the cache</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> response <span class="token punctuation">)</span> <span class="token keyword">return</span> response<br><br> <span class="token operator">/</span> <span class="token number">3.</span> If it isn’t<span class="token punctuation">,</span> fetch the file from the network and respond to the document <span class="token keyword">with</span> the fetched file<br> <span class="token keyword">return</span> <span class="token function">fetch</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>request<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>And because we are writing ES2015, we can shorten this into one glorious line -</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">event</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<br> event<span class="token punctuation">.</span><span class="token function">respondWith</span><span class="token punctuation">(</span> 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><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> response <span class="token operator">||</span> <span class="token function">fetch</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>request<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></code></pre> <p>Now, if we try to access any of the preached files while offline (or online), the page will load instantly with the cached files.</p> <p>That's it! Next week I will go over the second part of the &quot;Offline First&quot; model, using IndexedDB to store the dynamic data (in this case the articles). You can try using the application yourself at <a href="https://app.bitsofco.de">app.bitsofco.de</a>, or check out the source code on my <a href="https://github.com/ireade/app.bitsofco.de">GitHub repository</a>.</p> Alternative Text and Images 2016-10-04T00:00:00Z https://bitsofco.de/alternative-text-and-images/ <p>The most accessible format for any content on the web is plain text. Plain text is incredibly malleable, it can be altered in ways to cater to almost any disability. For users with no visual difficulties, they can simply read the text. If a user has visual impairments, the text can be converted to speech. Even if a user has both visual and auditory difficulties, plain text can be converted to braille.</p> <p>However, most websites are not made up of pure text. We have other types of content that is not so malleable, like images. To make these alternative forms of content more accessible, we need to provide a text alternative of them.</p> <p>For images, this is typically provided using the <code>alt</code> attribute. As a blanket rule, <strong>all images should have an <code>alt</code> attribute</strong>. This, however, does not mean that actual alternative text needs to be provided for every image. There are three states we can have for the <code>alt</code> attribute -</p> <ol> <li> <p><strong>Defined</strong> - Where actual descriptive text is provided in the attribute, e.g. <code>alt=&quot;A dog&quot;</code>. In this case, a screen reader will read the text provided when the user comes across the image, and usually give the context that it is an image.</p> </li> <li> <p><strong>Null</strong> - Where the attribute is written but left empty, e.g. <code>alt=&quot;&quot;</code>. In this case, a screen reader will skip over the image entirely as if it never existed.</p> </li> <li> <p><strong>Invalid</strong> (bad practice) - Where the attribute is skipped entirely. In this case, a screenreader will read the value of the <code>src</code> attribute instead. Having this happen is never useful to the user, so it is advised that no images remain in this state.</p> </li> </ol> <p>In this article, we will go over when each of the first two states is suitable, and how different types of alternative text should be used.</p> <h2 id="alternative-text-for-different-image-types" tabindex="-1">Alternative Text for Different Image Types <a class="header-anchor" href="https://bitsofco.de/alternative-text-and-images/">#</a></h2> <p>Broadly, there are 7 types of images. The way we handle alternative text is different for each.</p> <h3 id="1-images-of-text" tabindex="-1">1. Images of Text <a class="header-anchor" href="https://bitsofco.de/alternative-text-and-images/">#</a></h3> <p>As a general rule, images of text should be avoided, the only exception to this being for logos. Due to the flexibility of CSS, we can now recreate pretty much any visual styling to plain text. So, using an image of text may only be needed in the rarest of circumstances.</p> <p>In these rare cases, the best practice is to repeat the text of the image in the <code>alt</code> attribute. This will give screen reader users essentially the exact same experience as visual users.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/5otOKQ5oeU-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/5otOKQ5oeU-780.webp 780w"><img alt="Image with text &quot;The quick brown fox jumped over the lazy dog&quot;" loading="lazy" decoding="async" src="https://bitsofco.de/img/5otOKQ5oeU-780.jpeg" width="780" height="65"></picture></p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>example.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>The quick brown fox jumped over the lazy dog<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <h3 id="2-informative-images" tabindex="-1">2. Informative Images <a class="header-anchor" href="https://bitsofco.de/alternative-text-and-images/">#</a></h3> <p>Informative images are images that, as you would guess, provide some information. This information can be anything from a label, to supplementary information, to just an impression.</p> <p>For informative images, the alternative text should convey the same information that the image does. This might be as simple as one word saying what the image is. For example, in the image below, images are used to label each section.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/dz-75NXX_K-774.avif 774w"><source type="image/webp" srcset="https://bitsofco.de/img/dz-75NXX_K-774.webp 774w"><img alt="Details of a restaurant. Images are used for the headings Location, Cuisine and Opening Hours" loading="lazy" decoding="async" src="https://bitsofco.de/img/dz-75NXX_K-774.png" width="774" height="454"></picture></p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>location.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>Location<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>26 Eletu Ogabi...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>cutlery.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>Cuisine<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Continental<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>clock.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>Opening Hours<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Monday 12:00 - 22:00 ...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br></code></pre> <p>If the information that the image is conveying is more of an impression, then a physical description of the contents of the image may not be appropriate. Instead, we can provide the feeling that the image is trying to convey in the alternative text instead.</p> <p>For example, on a website for dog sitters, the image below might be on the about page.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/O0LS19oulc-1013.avif 1013w"><source type="image/webp" srcset="https://bitsofco.de/img/O0LS19oulc-1013.webp 1013w"><img alt="A woman playing with some dogs giving the impression that the dogs are having a great time" loading="lazy" decoding="async" src="https://bitsofco.de/img/O0LS19oulc-1013.jpeg" width="1013" height="504"></picture></p> <p><a href="https://www.postadsuk.com/reliable-dog-walker-dog-training-amp-dog-sitting-services-in-west-london-petsitters-amp-dogwalkers_982572-102.html">Image Source</a></p> <p>The purpose of this image in that context is to show that the dogs have fun while they are there, so the alternative text should convey this feeling.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>example.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>Your dogs will have the best time!<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <h3 id="3-functional-images" tabindex="-1">3. Functional Images <a class="header-anchor" href="https://bitsofco.de/alternative-text-and-images/">#</a></h3> <p>Functional images are those that represent an action to be taken. Unlike informative images, which purely provide information for the user, functional images are used convey something actionable. These images are also typically tied to interactive elements such as links or buttons. For functional images, the alternative text should clearly communicate the action that is to be taken.</p> <p>In the example below, the button used to filter the restaurants is represented by an image, so the alternative text displays the function of the button.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/HJa8_PodbX-813.avif 813w"><source type="image/webp" srcset="https://bitsofco.de/img/HJa8_PodbX-813.webp 813w"><img alt="A website displaying various restaurants. The restaurants can be filtered by pressing a button with an image of sliders" loading="lazy" decoding="async" src="https://bitsofco.de/img/HJa8_PodbX-813.png" width="813" height="269"></picture></p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>example.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>Filter Restaurants<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <h3 id="4-decorative-images" tabindex="-1">4. Decorative Images <a class="header-anchor" href="https://bitsofco.de/alternative-text-and-images/">#</a></h3> <p>Decorative images are images that don’t convey any actual/actionable information to the viewer. Their purpose is purely styling. For example, a background pattern on an element does not provide any informative value to the user.</p> <p>In these cases, an empty <code>alt</code> attribute should be provided. This allows the image to be simply skipped by the screenreader since it is of no use to a user with visual impairments.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>background.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><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <h3 id="5-complex-images" tabindex="-1">5. Complex Images <a class="header-anchor" href="https://bitsofco.de/alternative-text-and-images/">#</a></h3> <p>Complex images, a type of informative image, are those that contain a very detailed and substantial amount of information. For example bar charts or graphs. For these images, it is suggested that we provide both short and long descriptions.</p> <p>The short description should be provided via the <code>alt</code> attribute. The long description can be defined in some other element, and simply referenced using the <code>longdesc</code>/ <code>aria-labelledby</code> attributes, or by using the <code>&lt;figure&gt;</code> and <code>&lt;figcaption&gt;</code> elements.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/K1Kna0BBqB-662.avif 662w"><source type="image/webp" srcset="https://bitsofco.de/img/K1Kna0BBqB-662.webp 662w"><img alt="A complex line chart showing number of customers over a period of 12 weeks" loading="lazy" decoding="async" src="https://bitsofco.de/img/K1Kna0BBqB-662.png" width="662" height="335"></picture></p> <p>Here are the short and long descriptions for this image using the <code>&lt;figure&gt;</code> and <code>&lt;figcaption&gt;</code> elements -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>figure</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>group<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">&lt;</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>customers.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>Line chart showing the number of customers over a period of 12 weeks<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">&lt;</span>figcaption</span><span class="token punctuation">></span></span><br> In the first week, there were 13,000 customers. This slowly rose over the next few weeks, then rose drastically in week 4 to 37,000 customers. This was maintained until week 7, where there were less than 10,00 customers. In week 8, the number of customers rose again to 35,000, but this dropped back down in week 9. Since then, the number of customers has risen, and in week 12 there were 50,000 customers.<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>figcaption</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>figure</span><span class="token punctuation">></span></span></code></pre> <p>Alternatively, we can use the <code>longdesc</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">&lt;</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>customers.png<span class="token punctuation">"</span></span><br> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Line chart showing the number of customers over a period of 12 weeks<span class="token punctuation">"</span></span><br> <span class="token attr-name">longdesc</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#customers-chart-desc<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">&lt;</span>p</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>customers-chart-desc<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> In the first week, there were 13,000 customers. This slowly rose over the next few weeks, then rose drastically in week 4 to 37,000 customers. This was maintained until week 7, where there were less than 10,00 customers. In week 8, the number of customers rose again to 35,000, but this dropped back down in week 9. Since then, the number of customers has risen, and in week 12 there were 50,000 customers.<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre> <p>We can also achieve the same thing using the <code>aria-labelledby</code> attribute in place of <code>longdesc</code>, which works in the same way.</p> <h3 id="6-groups-of-images" tabindex="-1">6. Groups of Images <a class="header-anchor" href="https://bitsofco.de/alternative-text-and-images/">#</a></h3> <p>There are cases where more than one image is used together to convey a single piece of information. For example, a 5-star rating can be represented by a group of individual star images.</p> <p>In these cases, the information conveyed by the group of images together should be added to the <code>alt</code> text of just one of the images, and the rest should be left as null.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/u5R4Rn4xZ8-743.avif 743w"><source type="image/webp" srcset="https://bitsofco.de/img/u5R4Rn4xZ8-743.webp 743w"><img alt="An information card for a restaurant. Images of stars are used to represent the star rating" loading="lazy" decoding="async" src="https://bitsofco.de/img/u5R4Rn4xZ8-743.png" width="743" height="357"></picture></p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>star.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>4 out of 5 stars<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">&lt;</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>star.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><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">&lt;</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>star.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><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">&lt;</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>star.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><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <h3 id="7-image-maps" tabindex="-1">7. Image Maps <a class="header-anchor" href="https://bitsofco.de/alternative-text-and-images/">#</a></h3> <p>Finally, image maps are images that are divided into multiple sections each with their own interactive area. They are created by combining the <code>&lt;img&gt;</code> and <code>&lt;map&gt;</code> elements.</p> <p>For these types of images, it is advised that a defined <code>alt</code> attribute be provided for the <code>&lt;img&gt;</code> element, as well as each individual <code>&lt;area&gt;</code>.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/jFMqLeIuzm-500.avif 500w"><source type="image/webp" srcset="https://bitsofco.de/img/jFMqLeIuzm-500.webp 500w"><img alt="A map of the company board of directors and related staff" loading="lazy" decoding="async" src="https://bitsofco.de/img/jFMqLeIuzm-500.png" width="500" height="193"></picture></p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>orgchart.png<span class="token punctuation">"</span></span><br> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Board of directors and related staff: <span class="token punctuation">"</span></span><br> <span class="token attr-name">usemap</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#Map<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">&lt;</span>map</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>Map<span class="token punctuation">"</span></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>Map<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">&lt;</span>area</span> <span class="token attr-name">shape</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>rect<span class="token punctuation">"</span></span><br> <span class="token attr-name">coords</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>176,14,323,58<span class="token punctuation">"</span></span><br> <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><br> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Davy Jones: Chairman<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> […]<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>area</span> <span class="token attr-name">shape</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>rect<span class="token punctuation">"</span></span><br> <span class="token attr-name">coords</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>6,138,155,182<span class="token punctuation">"</span></span><br> <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><br> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Harry H Brown: Marketing Director (reports to chairman)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> [<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>map</span><span class="token punctuation">></span></span></code></pre> <p>Example from <a href="https://www.w3.org/WAI/tutorials/images/imagemap/">W3C WAI Tutorials</a></p> <h2 id="when-and-how-should-you-use-alt" tabindex="-1">When and How should you use <code>alt</code>? <a class="header-anchor" href="https://bitsofco.de/alternative-text-and-images/">#</a></h2> <p>As with everything, when and what <code>alt</code> to use is also dependent on the context, not just the type of image. When deciding what to use, we should ask the following questions -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/zfnRq5BdY_-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/zfnRq5BdY_-780.webp 780w"><img alt="Flow chart for choosing alternative text" loading="lazy" decoding="async" src="https://bitsofco.de/img/zfnRq5BdY_-780.png" width="780" height="656"></picture></p> <ol> <li><strong>Is the image of text?</strong> If yes, repeat the same text in the image in the alt attribute. For example, <code>alt=&quot;Same text as in image&quot;</code>.</li> <li><strong>Is the image purely decorative or for styling purposes?</strong> If yes, set the alternative text to null, so it will be ignored by screen readers. For example, <code>alt=&quot;&quot;</code>.</li> <li><strong>Does the image represent an action the user can take?</strong> If yes, write out the action that will be taken when the user interacts with the element. For example, <code>alt=&quot;Add to Cart&quot;</code>.</li> <li><strong>Is the image purely informative?</strong> If yes, the alternative text should convey the same information as the image. For example, <code>alt=&quot;Location&quot;</code>.</li> <li>Finally, <strong>does the image convey any information that is not also represented on the page as text?</strong> If yes, the alternative text should convey the same information as the image. If no, set the alternative text to null, so it will be ignored by screen readers.</li> </ol> Variable and Function Hoisting in ES2015 2016-09-27T00:00:00Z https://bitsofco.de/variable-and-function-hoisting-in-es2015/ <p>Variable hoisting is a behaviour in JavaScript where variable declarations are moved to the top of the scope (function scope or global scope) that the variable is defined within. The typical JavaScript variable can be created in two stages - <strong>declaration</strong> and <strong>initialisation</strong>.</p> <p>The declaration stage is where the variable reference is created, without any value. At this stage, if we try to access the variable value, we will get <code>undefined</code>. The second initialisation stage is where the actual value is assigned to the variable.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">exampleScope</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">var</span> foo<span class="token punctuation">;</span> <span class="token comment">// Declaration</span><br> foo <span class="token operator">=</span> ‘Hello<span class="token punctuation">,</span> world<span class="token operator">!</span>’<span class="token punctuation">;</span> <span class="token comment">// Initialisation</span><br><br> <span class="token keyword">var</span> bar <span class="token operator">=</span> ‘Hi there<span class="token operator">!</span>’<span class="token punctuation">;</span> <span class="token comment">// Declaration &amp; Initialisation in one step</span><br><span class="token punctuation">}</span></code></pre> <p>Hoisting is the behaviour of moving the declaration stage to the top of the scope, regardless of if the variable is declared and initialised in one step. This has always been a peculiar behaviour in JavaScript we just had to accept and work around. Fortunately, with the introduced of two new types of variables in ES2015, this behaviour is less of an issue.</p> <h2 id="hoisting-and-var" tabindex="-1">Hoisting and <code>var</code> <a class="header-anchor" href="https://bitsofco.de/variable-and-function-hoisting-in-es2015/">#</a></h2> <p>Prior to ES2015, <code>var</code> was the only form of variable we could use. The <code>var</code> variable is scoped to the function it sits within, or the global scope if it isn’t within a local function. Even with the introduction of <code>let</code> and <code>const</code> in ES2015, the old <code>var</code> still exists in its typical form.</p> <p>When a variable is created using <code>var</code>, regardless of how, the declaration of the variable is “hoisted” to the top of the scope. For example, consider the following function -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">exampleScope</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Other function stuff here</span><br> <span class="token keyword">var</span> foo <span class="token operator">=</span> ‘Hello<span class="token punctuation">,</span> world<span class="token operator">!</span>’<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>The function above will actually be interpreted as the following -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">exampleScope</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">var</span> foo<span class="token punctuation">;</span><br> <span class="token comment">// Other function stuff here</span><br> foo <span class="token operator">=</span> ‘Hello<span class="token punctuation">,</span> world<span class="token operator">!</span>’<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>At first glance, this may not seem to have a real effect on anything. However, it can lead to a very peculiar behaviour where variables can be used before they are actually created in our code.</p> <blockquote> <p>Hoisted variables can be accessed before they are initialised</p> </blockquote> <p>Typically, if we try to use a variable that has not been defined, we get a <code>ReferenceError</code>, which stops the rest of the script for executing.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">exampleScope</span><span class="token punctuation">(</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>foo<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => ReferenceError: foo is not defined [Stop script execution]</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>“This will never be logged”<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>However, if we declare and initialise the variable <em>after</em> trying to use it, we simply get <code>undefined</code>, a non-blocking error.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">exampleScope</span><span class="token punctuation">(</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>foo<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => undefined</span><br> <span class="token keyword">var</span> foo <span class="token operator">=</span> “Hello<span class="token punctuation">,</span> world<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>foo<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => “Hello, world!”</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>“This sentence will be logged”<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => “This sentence will be logged”</span><br><span class="token punctuation">}</span></code></pre> <p>Because of hoisting, the above code is actually equivalent to us having written the following -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">exampleScope</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">var</span> foo<span class="token punctuation">;</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>foo<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => undefined</span><br> foo <span class="token operator">=</span> “Hello<span class="token punctuation">,</span> world<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>foo<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => “Hello, world!”</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>“This sentence will be logged”<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => “This sentence will be logged”</span><br><span class="token punctuation">}</span></code></pre> <h2 id="hoisting-and-let" tabindex="-1">Hoisting and <code>let</code> <a class="header-anchor" href="https://bitsofco.de/variable-and-function-hoisting-in-es2015/">#</a></h2> <p>The <code>let</code> variable is a new form of variable introduced in ES2015. Instead of being scoped to the nearest function, <code>let</code> is scoped to the nearest block. This means that <code>let</code> variables defined within non-function blocks like <code>if</code> will only be available within those blocks.</p> <p>The <code>let</code> variable, like <code>var</code>, can be created in the two stages - declaration and initialisation. However, the hoisting behaviour does not apply. If we repeat the example above using <code>let</code> instead, we will get a ReferenceError because <code>foo</code> does not exist at the point it is trying to be used.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">exampleScope</span><span class="token punctuation">(</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>foo<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => ReferenceError: foo is not defined [Stop script execution]</span><br> <span class="token keyword">let</span> foo <span class="token operator">=</span> “Hello<span class="token punctuation">,</span> world<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>foo<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>“This will never be logged”<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="hoisting-and-const" tabindex="-1">Hoisting and <code>const</code> <a class="header-anchor" href="https://bitsofco.de/variable-and-function-hoisting-in-es2015/">#</a></h2> <p>The <code>const</code> variable is another new form of variable (technically, a constant) introduced in ES2015. Like <code>let</code>, it is scoped to the block, rather than the function. Unlike <code>let</code> and <code>var</code>, <code>const</code> must be declared and initialised in one step. Additionally, once this step is complete, the value cannot be modified.</p> <p>Because of this, hoisting does not apply to <code>const</code> either.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">exampleScope</span><span class="token punctuation">(</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>foo<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => ReferenceError: foo is not defined [Stop script execution]</span><br> <span class="token keyword">const</span> foo <span class="token operator">=</span> “Hello<span class="token punctuation">,</span> world<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>foo<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>“This will never be logged”<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="function-hoisting" tabindex="-1">Function Hoisting <a class="header-anchor" href="https://bitsofco.de/variable-and-function-hoisting-in-es2015/">#</a></h2> <p>In addition to variables, hoisting applies to functions created using the function declaration syntax.</p> <p>Function declarations, as with the <code>const</code> variable, are defined and initialised in one step. However, functions are hoisted by moving the entire function, not just the declaration, to the top of the scope. This means that functions can always be used before they are created, as long as the usage is within the same scope.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">exampleScope</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => “Hello, world!”</span><br><br> <span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</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>“Hello<span class="token punctuation">,</span> world<span class="token operator">!</span>”<span class="token punctuation">)</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p>Functions referenced by variables, on the other hand, are subject to the hoisting rules for the variable used. For example, if we create a function and assign it to a <code>var</code> variable, the declaration will be hoisted to the top of the scope. However, because it is only declared and not initialised as a function, we will not be able to call the function before the initialisation stage.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">exampleScope</span><span class="token punctuation">(</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>foo<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => undefined</span><br> <br> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// => TypeError: foo is not a function [Stop script execution]</span><br><br> <span class="token keyword">var</span> <span class="token function-variable function">foo</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</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>“Hello<span class="token punctuation">,</span> world<span class="token operator">!</span>”<span class="token punctuation">)</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <h2 id="what-does-this-mean-for-es2015" tabindex="-1">What does this mean for ES2015? <a class="header-anchor" href="https://bitsofco.de/variable-and-function-hoisting-in-es2015/">#</a></h2> <p>When writing ES2015, the best practice is to always use <code>const</code>. If you need the variable to be updated, then use <code>let</code>. There should be almost no circumstance in which using <code>var</code> is the better option, and so hoisting should not be an issue.</p> <blockquote> <p>Make let not var <a href="https://twitter.com/hashtag/javascript?src=hash">#javascript</a> <a href="https://twitter.com/hashtag/es2015?src=hash">#es2015</a> <a href="https://twitter.com/hashtag/es6?src=hash">#es6</a> <a href="https://t.co/IoJrKej83I">pic.twitter.com/IoJrKej83I</a> — Ire Aderinokun (@ireaderinokun) <a href="https://twitter.com/ireaderinokun/status/776050419684544512">September 14, 2016</a></p> </blockquote> <p>Nonetheless, function hoisting is still present, so this is a behaviour we should still be conscious of.</p> The New System Font Stack? 2016-09-20T00:00:00Z https://bitsofco.de/the-new-system-font-stack/ <p>A few months ago, I wrote about how you can use system fonts in the browser using the built-in keywords that work with the <code>font</code> shorthand property (see <a href="https://bitsofco.de/using-system-fonts-in-the-browser">Using System Fonts in the Browser</a>). These keywords can be useful, but they also have some drawbacks, the main one being that they only work with the shorthand <code>font</code> property. This means that we cannot use it in a list of font families with fallbacks or as a fallback itself.</p> <p>Relatively recently, some websites and web applications have been adopting a new method for using system fonts in the browser. With this method, the fonts used by different systems are explicitly called themselves in the <code>font-family</code> property.</p> <h2 id="the-font-stack" tabindex="-1">The Font Stack <a class="header-anchor" href="https://bitsofco.de/the-new-system-font-stack/">#</a></h2> <p>At the moment, there is not one universally-used sequence of fonts for this system font stack. However, the fonts used by different sites, although not exactly the same, overlap. These are the different fonts that have been used, and the devices (and browsers) they target -</p> <table> <thead> <tr> <th>Font</th> <th>Device Targeted</th> </tr> </thead> <tbody> <tr> <td><code>-apple-system</code> (San Francisco)</td> <td>iOS Safari, macOS Safari, macOS Firefox</td> </tr> <tr> <td><code>BlinkMacSystemFont</code> (San Francisco)</td> <td>macOS Chrome</td> </tr> <tr> <td><code>Segoe UI</code></td> <td>Windows</td> </tr> <tr> <td><code>Roboto</code></td> <td>Android, Chrome OS</td> </tr> <tr> <td><code>Oxygen</code> / <code>Oxygen-Sans</code></td> <td>KDE</td> </tr> <tr> <td><code>Fira Sans</code></td> <td>Firefox OS</td> </tr> <tr> <td><code>Droid Sans</code></td> <td>Older versions of Android</td> </tr> <tr> <td><code>Ubuntu</code></td> <td>Ubuntu</td> </tr> <tr> <td><code>Cantarell</code></td> <td>GNOME</td> </tr> <tr> <td><code>Helvetica Neue</code></td> <td>macOS versions &lt; 10.11</td> </tr> <tr> <td><code>Arial</code></td> <td>Any</td> </tr> <tr> <td><code>sans-serif</code></td> <td>Any</td> </tr> </tbody> </table> <p>The order in which the fonts are defined in the <code>font-family</code> property is very important. Because we are just passing font names to the property, the font that will be used is the first font available on the user's machine that matches that name. This means that we have to craft the stack in such a way that the first font available to the user is their system font, and not some font they have installed on their machine.</p> <p>We do this by starting with the most specific fonts possible, and moving on to fonts that are less guaranteed to be tied to a specific device. For example, the first font defined is always <code>-apple-system</code>. This will certainly only be applied to users on an iOS or macOS device. Further down the line, we have <code>Roboto</code>. This is a font that very well <em>could</em> be installed on a Mac user's device, but since the font stack started with the more specific <code>-apple-system</code>, we don't have to worry about this conflict.</p> <h3 id="examples" tabindex="-1">Examples <a class="header-anchor" href="https://bitsofco.de/the-new-system-font-stack/">#</a></h3> <p>Here is how this new system font stack is used across different websites. Starting with <strong>Wordpress 4.6</strong> -</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">font-family</span><span class="token punctuation">:</span> -apple-system<span class="token punctuation">,</span><br> BlinkMacSystemFont<span class="token punctuation">,</span><br> <span class="token string">"Segoe UI"</span><span class="token punctuation">,</span><br> Roboto<span class="token punctuation">,</span><br> Oxygen-Sans<span class="token punctuation">,</span><br> Ubuntu<span class="token punctuation">,</span><br> Cantarell<span class="token punctuation">,</span><br> <span class="token string">"Helvetica Neue"</span><span class="token punctuation">,</span><br> sans-serif<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><strong>Medium</strong> -</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">font-family</span><span class="token punctuation">:</span> -apple-system<span class="token punctuation">,</span><br> BlinkMacSystemFont<span class="token punctuation">,</span><br> <span class="token string">"Segoe UI"</span><span class="token punctuation">,</span><br> Roboto<span class="token punctuation">,</span><br> Oxygen<span class="token punctuation">,</span><br> Ubuntu<span class="token punctuation">,</span><br> Cantarell<span class="token punctuation">,</span><br> <span class="token string">"Open Sans"</span><span class="token punctuation">,</span><br> <span class="token string">"Helvetica Neue"</span><span class="token punctuation">,</span><br> sans-serif<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><strong>Ghost</strong> -</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">font-family</span><span class="token punctuation">:</span> -apple-system<span class="token punctuation">,</span><br> BlinkMacSystemFont<span class="token punctuation">,</span><br> <span class="token string">"Segoe UI"</span><span class="token punctuation">,</span><br> Roboto<span class="token punctuation">,</span><br> Oxygen<span class="token punctuation">,</span><br> Ubuntu<span class="token punctuation">,</span><br> Cantarell<span class="token punctuation">,</span><br> <span class="token string">"Fira Sans"</span><span class="token punctuation">,</span><br> <span class="token string">"Droid Sans"</span><span class="token punctuation">,</span><br> <span class="token string">"Helvetica Neue"</span><span class="token punctuation">,</span><br> sans-serif<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Finally, <strong>GitHub</strong> -</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">font-family</span><span class="token punctuation">:</span> -apple-system<span class="token punctuation">,</span><br> BlinkMacSystemFont<span class="token punctuation">,</span><br> <span class="token string">"Segoe UI"</span><span class="token punctuation">,</span><br> Roboto<span class="token punctuation">,</span><br> Helvetica<span class="token punctuation">,</span><br> Arial<span class="token punctuation">,</span><br> sans-serif<span class="token punctuation">,</span><br> <span class="token string">"Apple Color Emoji"</span><span class="token punctuation">,</span> <span class="token comment">/* Emojis*/</span><br> <span class="token string">"Segoe UI Emoji"</span><span class="token punctuation">,</span> <span class="token comment">/* Emojis*/</span><br> <span class="token string">"Segoe UI Symbol"</span><span class="token punctuation">;</span> <span class="token comment">/* Emojis*/</span><br><span class="token punctuation">}</span></code></pre> <p>Here is a comparison between the font stacks used by these sites -</p> <table> <thead> <tr> <th>Wordpress</th> <th>Medium</th> <th>Ghost</th> <th>Github</th> </tr> </thead> <tbody> <tr> <td><code>-apple-system</code></td> <td><code>-apple-system</code></td> <td><code>-apple-system</code></td> <td><code>-apple-system</code></td> </tr> <tr> <td><code>BlinkMacSystemFont</code></td> <td><code>BlinkMacSystemFont</code></td> <td><code>BlinkMacSystemFont</code></td> <td><code>BlinkMacSystemFont</code></td> </tr> <tr> <td><code>“Segoe UI”</code></td> <td><code>“Segoe UI”</code></td> <td><code>“Segoe UI”</code></td> <td><code>“Segoe UI”</code></td> </tr> <tr> <td><code>Roboto</code></td> <td><code>Roboto</code></td> <td><code>Roboto</code></td> <td><code>Roboto</code></td> </tr> <tr> <td><code>Oxygen-Sans</code></td> <td><code>Oxygen</code></td> <td><code>Oxygen</code></td> <td><code>Helvetica</code></td> </tr> <tr> <td><code>Ubuntu</code></td> <td><code>Ubuntu</code></td> <td><code>Ubuntu</code></td> <td><code>Arial</code></td> </tr> <tr> <td><code>Cantarell</code></td> <td><code>Cantarell</code></td> <td><code>Cantarell</code></td> <td><code>sans-serif</code></td> </tr> <tr> <td><code>“Helvetica Neue”</code></td> <td><code>“Open Sans”</code></td> <td><code>“Fira Sans”</code></td> <td></td> </tr> <tr> <td><code>sans-serif</code></td> <td><code>“Helvetica Neue”</code></td> <td><code>“Droid Sans”</code></td> <td></td> </tr> <tr> <td></td> <td><code>sans-serif</code></td> <td><code>“Helvetica Neue”</code></td> <td></td> </tr> <tr> <td></td> <td></td> <td><code>sans-serif</code></td> <td></td> </tr> </tbody> </table> <h2 id="why-use-this-font-stack" tabindex="-1">Why use this Font Stack? <a class="header-anchor" href="https://bitsofco.de/the-new-system-font-stack/">#</a></h2> <p>There are a number of benefits to using system fonts in general, and to this particular method of using them.</p> <h3 id="performance" tabindex="-1">Performance <a class="header-anchor" href="https://bitsofco.de/the-new-system-font-stack/">#</a></h3> <p>The number one reason for using system fonts at all is performance. Fonts are typically one of the largest/heaviest resources loaded on a website. If we can use a font already available on the user’s machine, we can completely eliminate the need to fetch this resource, making load times noticeably faster.</p> <h3 id="the-native-feel" tabindex="-1">The Native Feel <a class="header-anchor" href="https://bitsofco.de/the-new-system-font-stack/">#</a></h3> <p>Another benefit of using system fonts is that it creates a feeling of the website being in a native application. This makes this method particularly useful for web applications, such as Wordpress or GitHub, rather than websites, as the former may be trying to emulate native applications.</p> <h3 id="greater-control" tabindex="-1">Greater Control <a class="header-anchor" href="https://bitsofco.de/the-new-system-font-stack/">#</a></h3> <p>In comparison to using the built-in keywords with the <code>font</code> shorthand property, this method offers us a lot more control when specifying our fonts. We can define which font families we want to favour. Additionally, the other typography-related properties are not affected since this method only applies to the <code>font-family</code> property.</p> <h3 id="websites-don-t-need-to-look-the-same-everywhere" tabindex="-1">Websites Don't Need to Look the Same Everywhere <a class="header-anchor" href="https://bitsofco.de/the-new-system-font-stack/">#</a></h3> <p>Although the fact that different fonts will be shown for different users may seem like a drawback, in actual fact this doesn't matter. As <a href="https://twitter.com/mrmrs_/status/775837789619417088">mrmrs</a> aptly put it -</p> <blockquote> <p>Public service announcement:</p> <p>Your website doesn't need to look the same everywhere. It just needs to work everywhere. — murmurs (@mrmrs_) <a href="https://twitter.com/mrmrs_/status/775837789619417088">September 13, 2016</a></p> </blockquote> <h2 id="what-are-the-drawbacks" tabindex="-1">What are the Drawbacks? <a class="header-anchor" href="https://bitsofco.de/the-new-system-font-stack/">#</a></h2> <p>Using this method also has some drawbacks to consider.</p> <h3 id="naming-conflicts" tabindex="-1">Naming Conflicts <a class="header-anchor" href="https://bitsofco.de/the-new-system-font-stack/">#</a></h3> <p>Besides <code>-apple-system</code> and <code>BlinkMacSystemFont</code>, all we are doing is calling the actual name of fonts. This means that, if a user has a font by that same name installed on their machine that isn't their system font, that may be used instead. This is <a href="https://medium.design/system-shock-6b1dc6d6596f">what happened to Medium</a> when they first rolled out this method last year.</p> <h3 id="maintenance" tabindex="-1">Maintenance <a class="header-anchor" href="https://bitsofco.de/the-new-system-font-stack/">#</a></h3> <p>This list of fonts will have to be maintained and updated as the system fonts used by the various operating systems change. Hopefully, more operating systems will move towards Apple's method of having a generic name that will target the latest device font. But until then, a list will need to be periodically iterated on.</p> <h2 id="using-this-font-stack-today" tabindex="-1">Using this Font Stack Today <a class="header-anchor" href="https://bitsofco.de/the-new-system-font-stack/">#</a></h2> <p>If you would like to use this font stack today, which fonts you use will be dependent on your users. Personally, I think the Wordpress method is the most suitable for the largest amount of users, and has been <a href="https://make.wordpress.org/core/2016/07/07/native-fonts-in-4-6/">extensively tested by their team</a>.</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">font-family</span><span class="token punctuation">:</span> -apple-system<span class="token punctuation">,</span><br> BlinkMacSystemFont<span class="token punctuation">,</span><br> <span class="token string">"Segoe UI"</span><span class="token punctuation">,</span><br> Roboto<span class="token punctuation">,</span><br> Oxygen-Sans<span class="token punctuation">,</span><br> Ubuntu<span class="token punctuation">,</span><br> Cantarell<span class="token punctuation">,</span><br> <span class="token string">"Helvetica Neue"</span><span class="token punctuation">,</span><br> sans-serif<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Whichever method you choose, make sure to test it across the different browsers (and versions) that are important to the particular site.</p> JavaScript Promises 102 - The 4 Promise Methods 2016-09-13T00:00:00Z https://bitsofco.de/javascript-promises-102/ <p>As I covered in <a href="https://bitsofco.de/javascript-promises-101">JavaScript Promises 101</a>, a promise in JavaScript is an object that represents the result of an operation that hasn't been completed yet, but will at some undetermined point in the future.</p> <p>The Promise object itself (i.e. not an object on the prototype) has four methods available to it -</p> <ul> <li><code>resolve()</code></li> <li><code>reject()</code></li> <li><code>all()</code></li> <li><code>race()</code></li> </ul> <h2 id="promise-resolve" tabindex="-1">Promise.resolve() <a class="header-anchor" href="https://bitsofco.de/javascript-promises-102/">#</a></h2> <p>Typically, when we create promises, we create them as functions that perform some asynchronous operation which will resolve when the operation is complete (or reject if it fails). The <code>Promise.resolve()</code> method, however, returns a Promise object that is immediately resolved. It accepts one argument, a value that can be passed to the <code>.then()</code> function.</p> <p>Although this may seem counter to the purpose of promises, as there is no asynchronous operation being carried out, there are a few cases when this method is useful.</p> <h3 id="example-1-returning-static-values-as-promises" tabindex="-1">Example 1 - Returning Static Values as Promises <a class="header-anchor" href="https://bitsofco.de/javascript-promises-102/">#</a></h3> <p>One use case for the <code>Promise.resolve()</code> method is when we need to return a static value as a promise. To keep a chain of promises going, we need to return keep returning more promises. For example, in this sequence of fetch requests, we only move to the next <code>.then()</code> function when we return a promise that is resolved.</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token function">get</span><span class="token punctuation">(</span>url<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> response <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>response<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> secondURL <span class="token operator">=</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>url<br> <span class="token keyword">return</span> <span class="token function">get</span><span class="token punctuation">(</span> secondURL <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Return another Promise */</span><br><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">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> response <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>response<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> thirdURL <span class="token operator">=</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>url<br> <span class="token keyword">return</span> <span class="token function">get</span><span class="token punctuation">(</span> thirdURL <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Return another Promise */</span><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 function">handleError</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Note: The get() function is a promise-ified XMLHTTPRequest</span><br><span class="token comment">// (see https://bitsofco.de/javascript-promises-101/)</span></code></pre> <p>But what if, in the second step, we wanted to pass a value to the third step that was just static value, not a value received from a Promise? We can do that using <code>Promise.resolve()</code>, passing in the static value.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token function">get</span><span class="token punctuation">(</span>url<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> response <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>response<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> secondURL <span class="token operator">=</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>url<span class="token punctuation">;</span><br> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span> secondURL <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Return a manually created Promise */</span><br><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">secondURL</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">get</span><span class="token punctuation">(</span> secondURL <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 function">handleError</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>This way, we can keep the chain of promises going, even when working with regular functions or static values.</p> <h3 id="example-2-starting-chains-and-building-sequences" tabindex="-1">Example 2 - Starting Chains &amp; Building Sequences <a class="header-anchor" href="https://bitsofco.de/javascript-promises-102/">#</a></h3> <p>Another use case for the <code>Promise.resolve()</code> method is if we want to start a promise chain, but the initial value we need isn’t a promise. An example of this is if we want to dynamically create a promise sequence, and we need an initial promise to start it all off.</p> <p>Let's say we have an array of JSON files we need to get. In each of the files, there is an image url we need to retrieve and display on the page. So, for each item in the array of JSON files, we want to do the following (in the order they are in in the array) -</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token function">get</span><span class="token punctuation">(</span>url<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> response <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>response<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token function">displayImage</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span>image<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 are sure of the number of files we need to work with, we could just write out each of these tasks ourselves. But that wouldn't be very efficient, and what if we didn't know the exact number of files?</p> <p>To solve this problem, we can loop through each of the files, and create the above promise chain. But to make sure that we execute each of the files in sequence, we need to have an initial Promise to which we append the subsequent promise chains. This is what it looks like -</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token keyword">const</span> dataFiles <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'one.json'</span><span class="token punctuation">,</span> <span class="token string">'two.json'</span><span class="token punctuation">,</span> <span class="token string">'three.json'</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> sequence <span class="token operator">=</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Initial Promise</span><br><br>dataFiles<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">dataFile</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Add to the initial promise sequence</span><br> sequence <span class="token operator">=</span> sequence<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 keyword">return</span> <span class="token function">get</span><span class="token punctuation">(</span>url<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">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> response <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>response<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token function">displayImage</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span>image<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><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>Because we are appending to an existing promise, we needed that first resolved promise to get the chain started.</p> <h2 id="promise-reject" tabindex="-1">Promise.reject() <a class="header-anchor" href="https://bitsofco.de/javascript-promises-102/">#</a></h2> <p>The <code>Promise.reject()</code> method works like <code>Promise.resolve()</code>, but instead returns a promise object that is immediately rejected. It accepts one argument, the error that can be passed to the <code>.catch()</code> function.</p> <p>The primary use case for this method is similar to the first use case for <code>Promise.resolve()</code>. It can be used to throw errors based on static values. For example, in a promise chain, we may want to throw an error based on a simple check we make, which is not necessarily based on an asynchronous operation.</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token function">get</span><span class="token punctuation">(</span>url<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><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>length <span class="token operator">&lt;</span> <span class="token number">10</span> <span class="token comment">/* Can be any manual check */</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'Not enough data to continue'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> response <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>response<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> secondURL <span class="token operator">=</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>url<br> <span class="token keyword">return</span> <span class="token function">get</span><span class="token punctuation">(</span> secondURL <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 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> response <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>response<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">const</span> thirdURL <span class="token operator">=</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>url<br> <span class="token keyword">return</span> <span class="token function">get</span><span class="token punctuation">(</span> thirdURL <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 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 function">handleError</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <h2 id="promise-all" tabindex="-1">Promise.all() <a class="header-anchor" href="https://bitsofco.de/javascript-promises-102/">#</a></h2> <p>The <code>Promise.all()</code> method offers us a way of dealing with multiple promises together. The method accepts an iterable list of items as it’s only argument, executes any promises within them, and returns a promise.</p> <p>If all the promises within the iterable list are successfully resolved, it will return an array of the values to be read by the <code>.then()</code> function.</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token keyword">const</span> promise1 <span class="token operator">=</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</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 keyword">const</span> promise2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token keyword">function</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 punctuation">{</span><br> <span class="token function">setTimeout</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> <span class="token number">100</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><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> promise3 <span class="token operator">=</span> <span class="token string">'baz'</span><span class="token punctuation">;</span> <span class="token comment">// Doesn't actually have to be a promise</span><br><br>Promise<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">[</span>promise1<span class="token punctuation">,</span> promise2<span class="token punctuation">,</span> promise3<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">arrayOfResponses</span><span class="token punctuation">)</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>arrayOfResponses<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token comment">// ['foo', 'bar', 'baz']</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>If, however, even one of the promises in the iterable list is rejected, the entire promise is rejected.</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token keyword">const</span> promise1 <span class="token operator">=</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</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 keyword">const</span> promise2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token keyword">function</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 punctuation">{</span><br> <span class="token function">setTimeout</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> <span class="token number">100</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><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> promise3 <span class="token operator">=</span> Promise<span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span><span class="token string">'Error'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>Promise<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">[</span>promise1<span class="token punctuation">,</span> promise2<span class="token punctuation">,</span> promise3<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">arrayOfResponses</span><span class="token punctuation">)</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>arrayOfResponses<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">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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token comment">// 'Error'</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>This method is extremely useful when we have a group of asynchronous actions that are dependent on each other, so it becomes important that all be resolved together, or none at all.</p> <h2 id="promise-race" tabindex="-1">Promise.race() <a class="header-anchor" href="https://bitsofco.de/javascript-promises-102/">#</a></h2> <p>Finally, the <code>Promise.race()</code> method offers an alternative way of dealing with multiple promises. Like <code>Promise.all()</code>, it accepts an iterable list of items as it’s only argument, executes any promises within them, and returns a promise.</p> <p>However, this method will return as soon as any one of the promises in its list resolves or rejects, and passes the value or error message of that single promise to the <code>.then()</code> or <code>.catch()</code> function.</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token keyword">const</span> promise1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token keyword">function</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 punctuation">{</span><br> <span class="token function">setTimeout</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> <span class="token number">500</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 punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> promise2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token keyword">function</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 punctuation">{</span><br> <span class="token function">setTimeout</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> <span class="token number">100</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><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> promise3 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token keyword">function</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 punctuation">{</span><br> <span class="token function">setTimeout</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> <span class="token number">900</span><span class="token punctuation">,</span> <span class="token string">'baz'</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>Promise<span class="token punctuation">.</span><span class="token function">race</span><span class="token punctuation">(</span><span class="token punctuation">[</span>promise1<span class="token punctuation">,</span> promise2<span class="token punctuation">,</span> promise3<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">response</span><span class="token punctuation">)</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>response<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token comment">// bar</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>The use case for this method is less obvious than the others. From what I can tell the only scenario in which it will be useful is if you're trying to fetch the same resource from different places, so in this case you can deal with the response from whichever is the quickest, and discard the rest.</p> nth-child vs nth-of-type 2016-09-06T00:00:00Z https://bitsofco.de/nth-child-vs-nth-of-type/ <p>The <code>nth-child()</code> and <code>nth-of-type()</code> selectors are <a href="https://www.w3.org/TR/css3-selectors/#structural-pseudos">“structural” pseudo-classes</a>, which are classes that allow us to select elements based on information within the document tree that cannot typically be represented by other simple selectors.</p> <p>In the case of <code>nth-child()</code> and <code>nth-of-type()</code>, the extra information is the element’s position in the document tree in relation to its parent and siblings. Although these two pseudo-classes are very similar, they work in fundamentally different ways.</p> <h2 id="how-nth-child-works" tabindex="-1">How <code>nth-child()</code> Works <a class="header-anchor" href="https://bitsofco.de/nth-child-vs-nth-of-type/">#</a></h2> <p>The <code>nth-child()</code> pseudo-class is used to match an element based on a number, which represents the element’s position amongst it’s siblings. More specifically, the number represents the number of siblings that exist before the element in the document tree (minus 1).</p> <p>This number is expressed as a function, <code>an+b</code>, where <code>n</code> is the index, and <code>a</code> and <code>b</code> are any integers we pass. For example, to select every element, we could write any of the following -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">:nth-child(1n+0)</span> <span class="token punctuation">{</span> <span class="token comment">/* styles */</span> <span class="token punctuation">}</span><br><span class="token selector">:nth-child(n+0)</span> <span class="token punctuation">{</span> <span class="token comment">/* styles */</span> <span class="token punctuation">}</span><br><span class="token selector">:nth-child(1n)</span> <span class="token punctuation">{</span> <span class="token comment">/* styles */</span> <span class="token punctuation">}</span></code></pre> <p>In addition to using this function, we can pass a single integer, e.g. <code>:nth-child(1)</code> or use one of the set keywords, <code>odd</code> or <code>even</code>. These keywords are alternatives for writing out the functional notation for selecting every odd or even numbered element.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">:nth-child(odd)</span> <span class="token punctuation">{</span> <span class="token comment">/* styles for odd elements */</span> <span class="token punctuation">}</span><br><span class="token selector">:nth-child(2n+1)</span> <span class="token punctuation">{</span> <span class="token comment">/* styles for odd elements */</span> <span class="token punctuation">}</span><br><br><span class="token selector">:nth-child(even)</span> <span class="token punctuation">{</span> <span class="token comment">/* styles for even elements */</span> <span class="token punctuation">}</span><br><span class="token selector">:nth-child(2n+0)</span> <span class="token punctuation">{</span> <span class="token comment">/* styles for even elements */</span> <span class="token punctuation">}</span></code></pre> <p>When using <code>:nth-child()</code> on its own, it is simple enough to predict which element will be selected. For example, using this markup -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>example<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">&lt;</span>p</span><span class="token punctuation">></span></span>This is a <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">></span></span>paragraph<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">></span></span>.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>This is a <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">></span></span>paragraph<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">></span></span>.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>This is a <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">></span></span>paragraph<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">></span></span>.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span>This is a <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">></span></span>divider<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">></span></span>.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span>This is a <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">></span></span>divider<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">></span></span>.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- Element to select --></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>This is a <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">></span></span>paragraph<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">></span></span>.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>This is a <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">></span></span>paragraph<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">></span></span>.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span>This is a <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">></span></span>divider<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">></span></span>.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>This is a <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">></span></span>paragraph<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">></span></span>.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span>This is a <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">></span></span>divider<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">></span></span>.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p>If we wanted to select the fifth element, the div, we could simply write the following -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.example :nth-child(5)</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #ffdb3a<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/BZ1tjTWhJ1-772.avif 772w"><source type="image/webp" srcset="https://bitsofco.de/img/BZ1tjTWhJ1-772.webp 772w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/BZ1tjTWhJ1-772.png" width="772" height="284"></picture></p> <p>However, unexpected results may occur when there are multiple types of elements, and we need to combine the <code>:nth-child()</code> pseudo-class with type or class selectors. For example, to select that same div element again, we may be tempted to write the following -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.example div:nth-child(2)</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #ffdb3a<span class="token punctuation">;</span> <span class="token punctuation">}</span> </code></pre> <p>Note - Will not work</p> <p>The reason this will not work is because the element that selector is targeting does not actually exist. Using the above selector, the user agent will go through the following steps -</p> <ol> <li>Select all the child elements of <code>.example</code></li> <li>Find the second element in that list, irrespective of type</li> <li>Check if that element is a type of <code>div</code></li> </ol> <p>Since the second element in the document tree is a paragraph, not a div, nothing is selected. If we wanted to select the second div element, we would have to use the <code>nth-of-type()</code> pseudo-class.</p> <h2 id="how-nth-of-type-works" tabindex="-1">How <code>nth-of-type()</code> Works <a class="header-anchor" href="https://bitsofco.de/nth-child-vs-nth-of-type/">#</a></h2> <p>The <code>nth-of-type()</code> pseudo-class, like <code>nth-child()</code>, is used to match an element based on a number. This number, however, represents the element's position within <em>only those of its siblings that are of the same element type</em>.</p> <p>This number can also be expressed as a function, or using the keywords <code>even</code> or <code>odd</code>. Using the example markup above, we can select all the odd paragraphs by writing -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.example p:nth-of-type(odd)</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #ffdb3a<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/3aoOoApCMA-770.avif 770w"><source type="image/webp" srcset="https://bitsofco.de/img/3aoOoApCMA-770.webp 770w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/3aoOoApCMA-770.png" width="770" height="285"></picture></p> <p>When we use this selector, the user agent goes through the following steps -</p> <ol> <li>Select all the child elements of <code>.example</code> that are of the type <code>p</code></li> <li>Create a new list of only these elements</li> <li>Select the odd numbers from that list</li> </ol> <p>Because of this, we can now select the second div, the fifth child of <code>.example</code> -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.example div:nth-of-type(2)</span> <span class="token punctuation">{</span> <span class="token comment">/* styles */</span> <span class="token punctuation">}</span> </code></pre> <h2 id="other-nth-pseudo-classes" tabindex="-1">Other &quot;nth&quot; Pseudo-Classes <a class="header-anchor" href="https://bitsofco.de/nth-child-vs-nth-of-type/">#</a></h2> <p>Besides <code>nth-child()</code> and <code>nth-of-type()</code>, there are a number of other structural pseudo-classes we can use to select elements based on their position within their siblings. Like <code>nth-child()</code> and <code>nth-of-type()</code>, they fall into two groups - the ones that are <strong>type-independent</strong>, like <code>nth-child()</code>, and the ones that are <strong>type-dependent</strong>, like <code>nth-of-type()</code>.</p> <table> <thead> <tr> <th>Type-independent</th> <th>Type-dependent</th> </tr> </thead> <tbody> <tr> <td>nth-child()</td> <td>nth-of-type()</td> </tr> <tr> <td>nth-last-child()</td> <td>nth-last-of-type()</td> </tr> <tr> <td>first-child()</td> <td>first-of-type()</td> </tr> <tr> <td>last-child()</td> <td>last-of-type()</td> </tr> <tr> <td>only-child()</td> <td>only-of-type()</td> </tr> </tbody> </table> <h3 id="counting-from-the-end-nth-last-child-vs-nth-last-of-type" tabindex="-1">Counting From The End - <code>nth-last-child()</code> vs <code>nth-last-of-type()</code> <a class="header-anchor" href="https://bitsofco.de/nth-child-vs-nth-of-type/">#</a></h3> <p>These pseudo-classes work like <code>nth-child()</code> and <code>nth-of-type()</code>, but they begin counting from the last element in the group of siblings instead of the first.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.example :nth-last-child(1)</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #a6cae7<span class="token punctuation">;</span> <span class="token punctuation">}</span> <br><span class="token selector">.example p:nth-last-of-type(1)</span> <span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> #ffdb3a<span class="token punctuation">;</span> <span class="token punctuation">}</span> </code></pre> <h3 id="the-first-element-first-child-vs-first-of-type" tabindex="-1">The First Element - <code>first-child()</code> vs <code>first-of-type()</code> <a class="header-anchor" href="https://bitsofco.de/nth-child-vs-nth-of-type/">#</a></h3> <p>The <code>first-child()</code> and <code>first-of-type()</code> pseudo-classes select the first element. They can be thought of as using the <code>nth-child()</code> and <code>nth-of-type()</code> pseudo-classes, but simply passing in a value of 1.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.example :first-child()</span> <span class="token punctuation">{</span> <span class="token comment">/* styles */</span> <span class="token punctuation">}</span><br><span class="token selector">.example :nth-child(1)</span> <span class="token punctuation">{</span> <span class="token comment">/* styles */</span> <span class="token punctuation">}</span> <span class="token comment">/* same as above */</span><br><br><span class="token selector">.example :first-of-type()</span> <span class="token punctuation">{</span> <span class="token comment">/* styles */</span> <span class="token punctuation">}</span><br><span class="token selector">.example :nth-of-type(1)</span> <span class="token punctuation">{</span> <span class="token comment">/* styles */</span> <span class="token punctuation">}</span> <span class="token comment">/* same as above */</span></code></pre> <h3 id="the-last-element-last-child-vs-last-of-type" tabindex="-1">The Last Element - <code>last-child()</code> vs <code>last-of-type()</code> <a class="header-anchor" href="https://bitsofco.de/nth-child-vs-nth-of-type/">#</a></h3> <p>These are the opposite to the <code>first-child()</code> and <code>first-of-type()</code> pseudo-classes. They can be thought of as using the <code>nth-last-child()</code> and <code>nth-last-of-type()</code> pseudo-classes, but passing in a value of 1.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.example :last-child()</span> <span class="token punctuation">{</span> <span class="token comment">/* styles */</span> <span class="token punctuation">}</span><br><span class="token selector">.example :nth-last-child(1)</span> <span class="token punctuation">{</span> <span class="token comment">/* styles */</span> <span class="token punctuation">}</span> <span class="token comment">/* same as above */</span><br><br><span class="token selector">.example :last-of-type()</span> <span class="token punctuation">{</span> <span class="token comment">/* styles */</span> <span class="token punctuation">}</span><br><span class="token selector">.example :nth-last-of-type(1)</span> <span class="token punctuation">{</span> <span class="token comment">/* styles */</span> <span class="token punctuation">}</span> <span class="token comment">/* same as above */</span></code></pre> <h3 id="the-only-element-only-child-vs-only-of-type" tabindex="-1">The Only Element - <code>only-child()</code> vs <code>only-of-type()</code> <a class="header-anchor" href="https://bitsofco.de/nth-child-vs-nth-of-type/">#</a></h3> <p>Finally, these pseudo-classes will select an element that is the only child. For <code>only-child()</code>, the element must be literally the only child of it’s parent, regardless of type. For <code>only-of-type()</code>, the element need only be the only child of its type.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.example :only-child()</span> <span class="token punctuation">{</span> <span class="token comment">/* styles */</span> <span class="token punctuation">}</span><br><br><span class="token selector">.example p:only-of-type()</span> <span class="token punctuation">{</span> <span class="token comment">/* styles */</span> <span class="token punctuation">}</span></code></pre> Toast.js, a Library for Toast messages 2016-08-23T00:00:00Z https://bitsofco.de/toast-js-a-library-for-toast-messages-2/ <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/dhut5lwONq-640.avif 640w"><source type="image/gif" srcset="https://bitsofco.de/img/dhut5lwONq-640.gif 640w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/dhut5lwONq-640.webp" width="640" height="435"></picture></p> <p>Last week I created a small library for creating Toast messages. Here, I thought I would share my process in creating it and the code behind it.</p> <h2 id="the-html" tabindex="-1">The HTML <a class="header-anchor" href="https://bitsofco.de/toast-js-a-library-for-toast-messages-2/">#</a></h2> <p>The Toast is made up of two elements. The Toast message itself, <code>.toastjs</code>, and a container element, <code>.toastjs-container</code>.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>toastjs-container<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>alert<span class="token punctuation">"</span></span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<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">&lt;</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>toastjs<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token comment">&lt;!-- Content here --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p>Until a Toast is being displayed, the container is hidden using the <code>aria-hidden</code> attribute.</p> <h2 id="the-css" tabindex="-1">The CSS <a class="header-anchor" href="https://bitsofco.de/toast-js-a-library-for-toast-messages-2/">#</a></h2> <p>To create the sliding in/out animation of the Toast, a transform is applied to the Toast container element. By default, the <code>.toastjs-container</code> is positioned off-screen to the left.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.toastjs-container</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateX</span><span class="token punctuation">(</span>-150%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transition</span><span class="token punctuation">:</span> transform 1s<span class="token punctuation">;</span><br><br> <span class="token comment">/* Other styles */</span><br> <span class="token property">position</span><span class="token punctuation">:</span> fixed<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">left</span><span class="token punctuation">:</span> 30px<span class="token punctuation">;</span><br> <span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% - 60px<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">max-width</span><span class="token punctuation">:</span> 400px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>When the Toast message is being displayed, as is determined by it's <code>aria-hidden</code> attribute, the element is positioned to a normal state.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.toastjs-container[aria-hidden="false"]</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateX</span><span class="token punctuation">(</span>0%<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="the-javascript" tabindex="-1">The JavaScript <a class="header-anchor" href="https://bitsofco.de/toast-js-a-library-for-toast-messages-2/">#</a></h2> <p>The Toast is a custom <code>Toast</code> prototype. There are five main methods behind it.</p> <h3 id="toast-prototype-createelements" tabindex="-1">Toast.prototype._createElements <a class="header-anchor" href="https://bitsofco.de/toast-js-a-library-for-toast-messages-2/">#</a></h3> <p>This function creates the Toast element and it's container, with all the appropriate attributes and classes. It also appends it to the <code>body</code> element.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token class-name">Toast</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">_createElements</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</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><br> <span class="token keyword">this</span><span class="token punctuation">.</span>toastContainerEl <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">'div'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span>toastContainerEl<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">'toastjs-container'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span>toastContainerEl<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'role'</span><span class="token punctuation">,</span> <span class="token string">'alert'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span>toastContainerEl<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'aria-hidden'</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">this</span><span class="token punctuation">.</span>toastEl <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">'div'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span>toastEl<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">'toastjs'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">this</span><span class="token punctuation">.</span>toastContainerEl<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>toastEl<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><span class="token keyword">this</span><span class="token punctuation">.</span>toastContainerEl<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> <span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">500</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></code></pre> <h3 id="toast-prototype-addeventlisteners" tabindex="-1">Toast.prototype._addEventListeners <a class="header-anchor" href="https://bitsofco.de/toast-js-a-library-for-toast-messages-2/">#</a></h3> <p>This function adds event listeners to the buttons within the toast. By default, there is at least a close button. Optionally, there are custom buttons with custom callback functions that should be applied.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token class-name">Toast</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">_addEventListeners</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.toastjs-btn--close'</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 punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token keyword">this</span><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><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token keyword">this</span><span class="token punctuation">.</span>options<span class="token punctuation">.</span>customButtons <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">const</span> customButtonsElArray <span class="token operator">=</span> <span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">.</span><span class="token function">call</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">'.toastjs-btn--custom'</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">;</span><br> customButtonsElArray<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">el<span class="token punctuation">,</span> index</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> el<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">event</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">this</span><span class="token punctuation">.</span>options<span class="token punctuation">.</span>customButtons<span class="token punctuation">[</span>index<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">onClick</span><span class="token punctuation">(</span>event<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 punctuation">}</span><span class="token punctuation">;</span></code></pre> <h3 id="toast-prototype-close" tabindex="-1">Toast.prototype._close <a class="header-anchor" href="https://bitsofco.de/toast-js-a-library-for-toast-messages-2/">#</a></h3> <p>This is the function that is called when the Toast is closed. It first sets the correct <code>aria-hidden</code> attribute on the Toast container, then resets the main Toast element.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token class-name">Toast</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">_close</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</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">this</span><span class="token punctuation">.</span>toastContainerEl<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'aria-hidden'</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span><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> <span class="token punctuation">{</span><br><br> <span class="token keyword">this</span><span class="token punctuation">.</span>toastEl<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">;</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span>toastEl<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">'default'</span><span class="token punctuation">,</span> <span class="token string">'success'</span><span class="token punctuation">,</span> <span class="token string">'warning'</span><span class="token punctuation">,</span> <span class="token string">'danger'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token keyword">this</span><span class="token punctuation">.</span>focusedElBeforeOpen <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span>focusedElBeforeOpen<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> <span class="token punctuation">}</span><br><br> <span class="token function">resolve</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 number">1000</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></code></pre> <h3 id="toast-prototype-open" tabindex="-1">Toast.prototype._open <a class="header-anchor" href="https://bitsofco.de/toast-js-a-library-for-toast-messages-2/">#</a></h3> <p>This is the function that is called when the Toast is opened. It does the following things -</p> <ul> <li>Sets the correct <code>aria-hidden</code> attribute to the Toast container</li> <li>Adds the correct class to the Toast element</li> <li>Adds the correct message content to the Toast element</li> <li>Creates any custom buttons if specified</li> </ul> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token class-name">Toast</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">_open</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">this</span><span class="token punctuation">.</span>toastEl<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 keyword">this</span><span class="token punctuation">.</span>options<span class="token punctuation">.</span>type<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span>toastContainerEl<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'aria-hidden'</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">let</span> customButtons <span class="token operator">=</span> <span class="token string">''</span><span class="token punctuation">;</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token keyword">this</span><span class="token punctuation">.</span>options<span class="token punctuation">.</span>customButtons <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> customButtons <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>options<span class="token punctuation">.</span>customButtons<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">customButton<span class="token punctuation">,</span> index</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 template-string"><span class="token template-punctuation string">`</span><span class="token string">&lt;button type="button" class="toastjs-btn toastjs-btn--custom"></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>customButton<span class="token punctuation">.</span>text<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/button></span><span class="token template-punctuation string">`</span></span><br> <span class="token punctuation">}</span> <span class="token punctuation">)</span><br> customButtons <span class="token operator">=</span> customButtons<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><br> <span class="token punctuation">}</span><br><br> <span class="token keyword">this</span><span class="token punctuation">.</span>toastEl<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> &lt;p></span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>options<span class="token punctuation">.</span>message<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">&lt;/p><br> &lt;button type="button" class="toastjs-btn toastjs-btn--close">Close&lt;/button><br> </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>customButtons<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><br><br> <span class="token keyword">this</span><span class="token punctuation">.</span>focusedElBeforeOpen <span class="token operator">=</span> document<span class="token punctuation">.</span>activeElement<span class="token punctuation">;</span><br> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.toastjs-btn--close'</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><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre> <h3 id="toast-prototype-init" tabindex="-1">Toast.prototype._init <a class="header-anchor" href="https://bitsofco.de/toast-js-a-library-for-toast-messages-2/">#</a></h3> <p>Finally, this is the function that initialises the Toast. It calls all the other functions in their correct sequence.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token class-name">Toast</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">_init</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> Promise<span class="token punctuation">.</span><span class="token function">resolve</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 keyword">if</span> <span class="token punctuation">(</span> <span class="token keyword">this</span><span class="token punctuation">.</span>toastContainerEl <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">_createElements</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><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 keyword">if</span> <span class="token punctuation">(</span> <span class="token keyword">this</span><span class="token punctuation">.</span>toastContainerEl<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'aria-hidden'</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token string">'false'</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> <span class="token keyword">this</span><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><br> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</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><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 keyword">this</span><span class="token punctuation">.</span><span class="token function">_open</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">_addEventListeners</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><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre> <p>You can view the <a href="https://github.com/ireade/Toast.js">full source for the library here</a>, or <a href="https://ireade.github.io/Toast.js/">view a demo</a>.</p> Making botsofcode - An Introduction to Twitter Bots 2016-08-16T00:00:00Z https://bitsofco.de/making-botsofcode-an-introduction-to-twitter-bots/ <p>Another addition to my bot army! This time, I made a very simple twitter bot for this blog, <a href="https://twitter.com/botsofcode">@botsofcode</a> (<a href="https://github.com/ireade/botsofcode">Source Code</a>).</p> <h2 id="the-setup" tabindex="-1">The Setup <a class="header-anchor" href="https://bitsofco.de/making-botsofcode-an-introduction-to-twitter-bots/">#</a></h2> <p>I created @botsofcode using <a href="https://github.com/ttezel/twit">Twit</a>, a simple Twitter API Client for Node.js. To setup Twit, we need to first create a <a href="https://apps.twitter.com/">Twitter Application</a> and get four private keys.</p> <ul> <li>Consumer Key (API Key)</li> <li>Consumer Secret (API Secret)</li> <li>Access Token</li> <li>Access Token Secret</li> </ul> <p>Once the application is created, these keys can be found under the ‘Keys and Access Tokens’ section.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/NDHCQERz4d-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/NDHCQERz4d-780.webp 780w"><img alt="Twitter API Page section showing the four private keys" loading="lazy" decoding="async" src="https://bitsofco.de/img/NDHCQERz4d-780.png" width="780" height="548"></picture></p> <p>Once we have these keys, we can create a new Node.js project and initialise Twit.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> Twit <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'twit'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">const</span> <span class="token constant">T</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Twit</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">consumer_key</span><span class="token operator">:</span> <span class="token constant">APPLICATION_CONSUMER_KEY_HERE</span><span class="token punctuation">,</span><br> <span class="token literal-property property">consumer_secret</span><span class="token operator">:</span> <span class="token constant">APPLICATION_CONSUMER_SECRET_HERE</span><span class="token punctuation">,</span><br> <span class="token literal-property property">access_token</span><span class="token operator">:</span> <span class="token constant">ACCESS_TOKEN_HERE</span><span class="token punctuation">,</span><br> <span class="token literal-property property">access_token_secret</span><span class="token operator">:</span> <span class="token constant">ACCESS_TOKEN_SECRET_HERE</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <h2 id="listening-for-events-and-keywords" tabindex="-1">Listening for Events and Keywords <a class="header-anchor" href="https://bitsofco.de/making-botsofcode-an-introduction-to-twitter-bots/">#</a></h2> <p>Twitter's <a href="https://dev.twitter.com/streaming/overview">Streaming APIs</a> gives us access to the global stream of tweets. There are two streams we can currently follow -</p> <ul> <li>The <strong>Public Stream</strong>, which is the stream of all public tweets</li> <li>The <strong>User Stream</strong>, which is a stream of tweets corresponding to a single user's view</li> </ul> <p>@botsofcode uses the public stream, because it listens for tweets from any user that mention this blog.</p> <p>We can create a stream of tweets based on a filter using the <code>statuses/filter</code> endpoint and passing an object with the filter parameters. We can pass parameters like language or location, but to filter by keyword, we use the <code>track</code> parameter. This accepts a string or array of keywords to watch for.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> stream <span class="token operator">=</span> <span class="token constant">T</span><span class="token punctuation">.</span><span class="token function">stream</span><span class="token punctuation">(</span><span class="token string">'statuses/filter'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">track</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'bitsofco.de'</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 punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>Once the stream is created, we can listen for any tweets that fall within the stream.</p> <pre class="language-js" tabindex="0"><code class="language-js">stream<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'tweet'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">tweet</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><br> <span class="token comment">// Do something with the tweet</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br></code></pre> <h2 id="responding-to-events" tabindex="-1">Responding to Events <a class="header-anchor" href="https://bitsofco.de/making-botsofcode-an-introduction-to-twitter-bots/">#</a></h2> <p>Twitter's REST API allows us to take pretty much any action you could if you were using twitter directly. We can post tweets, retweet, reply to tweets, follow users, favourite/like, etc.</p> <p>I built @botsofcode to be able to take four actions - retweet, like, reply, and add to a list.</p> <h3 id="retweet" tabindex="-1">Retweet <a class="header-anchor" href="https://bitsofco.de/making-botsofcode-an-introduction-to-twitter-bots/">#</a></h3> <p>If the tweet found from the stream was from myself (<a href="https://twitter.com/ireaderinokun">@ireaderinokun</a>), @botsofcode should just retweet it. We can retweet by posting to the <code>/statuses/retweet/:id</code> endpoint.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> me <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">id</span><span class="token operator">:</span> <span class="token number">2714960622</span><span class="token punctuation">,</span><br> <span class="token literal-property property">screen_name</span><span class="token operator">:</span> <span class="token string">'ireaderinokun'</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span><br><br><span class="token keyword">const</span> stream <span class="token operator">=</span> <span class="token constant">T</span><span class="token punctuation">.</span><span class="token function">stream</span><span class="token punctuation">(</span><span class="token string">'statuses/filter'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">track</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'bitsofco.de'</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 punctuation">)</span><span class="token punctuation">;</span><br>stream<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'tweet'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">tweet</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> tweet<span class="token punctuation">.</span>user<span class="token punctuation">.</span>id <span class="token operator">===</span> me<span class="token punctuation">.</span>id <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token constant">T</span><span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'statuses/retweet/:id'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">id</span><span class="token operator">:</span> tweet<span class="token punctuation">.</span>id_str <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</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="like" tabindex="-1">Like <a class="header-anchor" href="https://bitsofco.de/making-botsofcode-an-introduction-to-twitter-bots/">#</a></h3> <p>If the tweet was from any other account, like it. We can like a tweet by posting to the <code>/favorites/create</code> endpoint, passing the id of the tweet we want to like.</p> <pre class="language-js" tabindex="0"><code class="language-js">stream<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'tweet'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">tweet</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> tweet<span class="token punctuation">.</span>user<span class="token punctuation">.</span>id <span class="token operator">===</span> me<span class="token punctuation">.</span>id <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 constant">T</span><span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'favorites/create'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">id</span><span class="token operator">:</span> tweet<span class="token punctuation">.</span>id_str <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="reply" tabindex="-1">Reply <a class="header-anchor" href="https://bitsofco.de/making-botsofcode-an-introduction-to-twitter-bots/">#</a></h3> <p>If the tweet was by any other user, send them a reply. We can reply to a tweet by posting to the <code>/statuses/update</code> endpoint and passing the id of the tweet we are replying to.</p> <pre class="language-js" tabindex="0"><code class="language-js">stream<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'tweet'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">tweet</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> tweet<span class="token punctuation">.</span>user<span class="token punctuation">.</span>id <span class="token operator">===</span> me<span class="token punctuation">.</span>id <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 constant">T</span><span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'favorites/create'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">id</span><span class="token operator">:</span> tweet<span class="token punctuation">.</span>id_str <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token constant">T</span><span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'statuses/update'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">status</span><span class="token operator">:</span> `@$<span class="token punctuation">{</span>tweet<span class="token punctuation">.</span>user<span class="token punctuation">.</span>screen_name<span class="token punctuation">}</span> Thanks <span class="token keyword">for</span> sharing<span class="token operator">!</span> $<span class="token punctuation">{</span>emojis<span class="token punctuation">[</span>Math<span class="token punctuation">.</span><span class="token function">floor</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> emojis<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">]</span>`<span class="token punctuation">,</span><br> <span class="token literal-property property">in_reply_to_status_id</span><span class="token operator">:</span> tweet<span class="token punctuation">.</span>id_str<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><span class="token punctuation">;</span></code></pre> <h3 id="add-to-list" tabindex="-1">Add to List <a class="header-anchor" href="https://bitsofco.de/making-botsofcode-an-introduction-to-twitter-bots/">#</a></h3> <p>Finally, add the user to a twitter list. We can do this by posting to the <code>lists/members/create</code> endpoint and passing the list parameters.</p> <pre class="language-js" tabindex="0"><code class="language-js">stream<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'tweet'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">tweet</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> tweet<span class="token punctuation">.</span>user<span class="token punctuation">.</span>id <span class="token operator">===</span> me<span class="token punctuation">.</span>id <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 constant">T</span><span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'favorites/create'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">id</span><span class="token operator">:</span> tweet<span class="token punctuation">.</span>id_str <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token constant">T</span><span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'statuses/update'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">status</span><span class="token operator">:</span> `@$<span class="token punctuation">{</span>tweet<span class="token punctuation">.</span>user<span class="token punctuation">.</span>screen_name<span class="token punctuation">}</span> Thanks <span class="token keyword">for</span> sharing<span class="token operator">!</span> $<span class="token punctuation">{</span>emojis<span class="token punctuation">[</span>Math<span class="token punctuation">.</span><span class="token function">floor</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> emojis<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">]</span>`<span class="token punctuation">,</span><br> <span class="token literal-property property">in_reply_to_status_id</span><span class="token operator">:</span> tweet<span class="token punctuation">.</span>id_str<br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token constant">T</span><span class="token punctuation">.</span><span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'lists/members/create'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">slug</span><span class="token operator">:</span> <span class="token string">'bitsofcoders'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">owner_screen_name</span><span class="token operator">:</span> botsofcode<span class="token punctuation">.</span>screen_name<span class="token punctuation">,</span><br> <span class="token literal-property property">screen_name</span><span class="token operator">:</span> tweet<span class="token punctuation">.</span>user<span class="token punctuation">.</span>screen_name<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><span class="token punctuation">;</span></code></pre> <h2 id="deployment" tabindex="-1">Deployment <a class="header-anchor" href="https://bitsofco.de/making-botsofcode-an-introduction-to-twitter-bots/">#</a></h2> <p>I used Heroku to host @botsofcode. Deploying a Node.js app to Heroku is simple, we just need to make sure that the following information is specified in our <code>package.json</code> file -</p> <ul> <li>The main script</li> <li>The start script</li> <li>Any dependencies</li> <li>The version of Node.js</li> </ul> <p>This is my <code>package.json</code> file for the bot -</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">"botsofcode"</span><span class="token punctuation">,</span><br> <span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"1.0.0"</span><span class="token punctuation">,</span><br> <span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"A bot for bitsofco.de"</span><span class="token punctuation">,</span><br> <span class="token property">"main"</span><span class="token operator">:</span> <span class="token string">"index.js"</span><span class="token punctuation">,</span><br> <span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token property">"start"</span><span class="token operator">:</span> <span class="token string">"node index.js"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token property">"author"</span><span class="token operator">:</span> <span class="token string">"ire-aderinokun"</span><span class="token punctuation">,</span><br> <span class="token property">"dependencies"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token property">"twit"</span><span class="token operator">:</span> <span class="token string">"^2.2.4"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token property">"engines"</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token property">"node"</span><span class="token operator">:</span> <span class="token string">"4.1.1"</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <h3 id="optional-setting-up-config-variables" tabindex="-1">Optional - Setting up Config Variables <a class="header-anchor" href="https://bitsofco.de/making-botsofcode-an-introduction-to-twitter-bots/">#</a></h3> <p>To keep the Twitter Application keys out of the app files, I used Heroku's config variables. These can be defined under the application settings.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Wu9Nt8QHHO-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/Wu9Nt8QHHO-780.webp 780w"><img alt="Heroku Application Conifg Variables" loading="lazy" decoding="async" src="https://bitsofco.de/img/Wu9Nt8QHHO-780.png" width="780" height="298"></picture></p> <p>In the actual application script, we can then reference them as environment variables -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">const</span> <span class="token constant">T</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Twit</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">consumer_key</span><span class="token operator">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span>consumer_key<span class="token punctuation">,</span><br> <span class="token literal-property property">consumer_secret</span><span class="token operator">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span>consumer_secret<span class="token punctuation">,</span><br> <span class="token literal-property property">access_token</span><span class="token operator">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span>access_token<span class="token punctuation">,</span><br> <span class="token literal-property property">access_token_secret</span><span class="token operator">:</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span>access_token_secret<br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>That's it! You can <a href="https://twitter.com/botsofcode">follow @botsofcode</a>, <a href="https://github.com/ireade/botsofcode">view it's source</a>, or click the tweet button below to see it in action.</p> JavaScript Array Methods - Mutator Methods 2016-08-09T00:00:00Z https://bitsofco.de/javascript-array-methods-mutator-methods/ <p>Whenever an array is created, we have access to some native methods we can use to with the array. There are currently over 30 of these methods. In this video, which is the first part in this series, I go over the &quot;mutator methods&quot;, which are methods that will modify the original array when used. These are -</p> <ul> <li><code>Array.prototype.pop()</code></li> <li><code>Array.prototype.shift()</code></li> <li><code>Array.prototype.push()</code></li> <li><code>Array.prototype.unshift()</code></li> <li><code>Array.prototype.reverse()</code></li> <li><code>Array.prototype.sort()</code></li> <li><code>Array.prototype.splice()</code></li> <li><code>Array.prototype.fill()</code></li> <li><code>Array.prototype.copyWithin()</code></li> </ul> <p><a href="https://www.youtube.com/watch?v=uXV7KwRubwM"><picture><source type="image/avif" srcset="https://bitsofco.de/img/-DrOF7WHTB-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/-DrOF7WHTB-780.webp 780w"><img alt="JavaScript Array Methods - Mutator" loading="lazy" decoding="async" src="https://bitsofco.de/img/-DrOF7WHTB-780.png" width="780" height="439"></picture></a></p> <p>You can get the files I worked with in this video in my <a href="https://github.com/ireade/array-methods/tree/master/mutator">repository</a>.</p> Creating An Accessible Modal Dialog 2016-08-02T00:00:00Z https://bitsofco.de/accessible-modal-dialog/ <p>Modal Dialogs are a tricky thing to make accessible. For visual users navigating with a mouse, creating a dialog is as simple as styling the element to look visually different from the rest of the page. However, users navigating a site via a keyboard and/or screenreader need a lot more.</p> <p>Luckily, the <a href="https://www.w3.org/TR/wai-aria/">WAI-ARIA Specification</a> provides some guidelines on how to build an accessible modal dialog. Their guidelines can be broken up into size parts -</p> <ol> <li>Markup the Dialog and Dialog Overlay Appropriately</li> <li>On Dialog Open, Set Focus</li> <li>On Dialog Close, Return Focus to the Last Focused Element</li> <li>While Open, Prevent Mouse Clicks Outside the Dialog</li> <li>While Open, Prevent Tabbing to Outside the Dialog</li> <li>While Open?</li> <li>Allow the <code>ESC</code> Key to Close the Dialog</li> </ol> <h2 id="1-markup-the-dialog-and-dialog-overlay-appropriately" tabindex="-1">1. Markup the Dialog and Dialog Overlay Appropriately <a class="header-anchor" href="https://bitsofco.de/accessible-modal-dialog/">#</a></h2> <h3 id="the-dialog" tabindex="-1">The Dialog <a class="header-anchor" href="https://bitsofco.de/accessible-modal-dialog/">#</a></h3> <blockquote> <p>Dialogs should have an appropriate <code>role</code> and label</p> </blockquote> <p>When creating a dialog, we need to make sure that it has the appropriate role, in this case <code>dialog</code>. We also need to make sure that there is a label for the dialog, provided using the <code>aria-labelledby</code> and optionally the <code>aria-describedby</code> attributes (see <a href="https://bitsofco.de/html-for-screen-readers-labelling-elements">HTML for Screen Readers</a>).</p> <p>Neither of the labels need to be visually displayed on the screen, as long as they are in the document tree.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>dialog<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>dialog<span class="token punctuation">"</span></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>dialog-title<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>dialog-description<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">&lt;</span>h1</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>dialog-title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Site Navigation<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</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>dialog-description<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>sr-only<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Description goes here<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>nav</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>one.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Link One<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>two.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Link Two<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>three.html<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Link Three<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>nav</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Close Navigation<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>close-dialog<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>i</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>fa fa-times<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>i</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <h3 id="the-overlay" tabindex="-1">The Overlay <a class="header-anchor" href="https://bitsofco.de/accessible-modal-dialog/">#</a></h3> <p>The dialog overlay should be a separate element, preferably a child of the <code>&lt;body&gt;</code> element (we will see why in a following section). It does not require any special ARIA attributes.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“body-content”</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“dialog”</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“dialog-overlay”</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span></code></pre> <h2 id="2-on-dialog-open-set-focus" tabindex="-1">2. On Dialog Open, Set Focus <a class="header-anchor" href="https://bitsofco.de/accessible-modal-dialog/">#</a></h2> <blockquote> <p>When first opened, focus should be set to the first focusable element within the dialog</p> </blockquote> <p>To achieve this, we need to generate an array of all the focusable elements within the dialog. Elements that can receive focus are anchors, form input elements, buttons, and any element with a <code>tabindex</code> of <code>0</code> or greater. We can get the array by querying the document for all of these types of elements -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">Dialog</span><span class="token punctuation">(</span><span class="token parameter">dialogEl<span class="token punctuation">,</span> overlayEl</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">this</span><span class="token punctuation">.</span>dialogEl <span class="token operator">=</span> dialogEl<span class="token punctuation">;</span><br><br> <span class="token keyword">var</span> focusableEls <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>dialogEl<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [tabindex="0"]'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span>focusableEls <span class="token operator">=</span> <span class="token class-name">Array</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span>focusableEls<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>We can then get the first of these focusable elements and use the <code>focus()</code> method to direct focus to it.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">this</span><span class="token punctuation">.</span>firstFocusableEl <span class="token operator">=</span> focusableEls<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">this</span><span class="token punctuation">.</span>firstFocusableEl<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>There should be always at least one focusable element in the dialog. If there are no focusable elements within the content of the dialog, there should still be the button that closes the dialog itself.</p> <h3 id="3-on-dialog-close-return-focus-to-last-focused-element" tabindex="-1">3. On Dialog Close, Return Focus to Last Focused Element <a class="header-anchor" href="https://bitsofco.de/accessible-modal-dialog/">#</a></h3> <blockquote> <p>When the dialog is closed, focus should be returned to the element that opened it</p> </blockquote> <p>When the dialog is first opened, we can determine which element opened it by checking the active element in the document (before we redirect focus away).</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token class-name">Dialog</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">open</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span>focusedElBeforeOpen <span class="token operator">=</span> document<span class="token punctuation">.</span>activeElement<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>When the dialog is then closed, we can easily set that element to focus.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token class-name">Dialog</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">close</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span>focusedElBeforeOpen<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><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre> <h2 id="4-while-open-prevent-mouse-clicks-outside-the-dialog" tabindex="-1">4. While Open, Prevent Mouse Clicks Outside the Dialog <a class="header-anchor" href="https://bitsofco.de/accessible-modal-dialog/">#</a></h2> <blockquote> <p>Users should not be able to click on elements outside the dialog window</p> </blockquote> <p>We can achieve this with some simple styling of the dialog overlay element. First, we position the overlay on the z-axis in-between the dialog itself and the rest of the body content -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.dialog</span> <span class="token punctuation">{</span> <span class="token property">z-index</span><span class="token punctuation">:</span> 3<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.dialog-overlay</span> <span class="token punctuation">{</span> <span class="token property">z-index</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.body-content</span> <span class="token punctuation">{</span> <span class="token property">z-index</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p>This is where it helps that these three elements are directly children of the <code>&lt;body&gt;</code>. Next, we need to make sure that the overlay takes up the entire viewport -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.dialog-overlay</span> <span class="token punctuation">{</span><br> <span class="token property">position</span><span class="token punctuation">:</span> fixed<span class="token punctuation">;</span><br> <span class="token property">top</span><span class="token punctuation">:</span> 0<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">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> <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.7<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Finally, in our HTML, we set a <code>tabindex</code> of <code>-1</code> on the overlay to prevent it from receiving focus.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“body-content”</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“dialog”</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“dialog-overlay”</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><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span></code></pre> <h2 id="5-while-open-prevent-tabbing-to-outside-the-dialog" tabindex="-1">5. While Open, Prevent Tabbing to Outside the Dialog <a class="header-anchor" href="https://bitsofco.de/accessible-modal-dialog/">#</a></h2> <blockquote> <p>User navigating with a keyboard should not be able to<code>TAB</code> out of the dialog content</p> </blockquote> <p>Although <a href="https://www.w3.org/TR/UNDERSTANDING-WCAG20/keyboard-operation-trapping.html">keyboard traps are typically discouraged</a>, modal dialogs are one scenario in which they are necessary. Because a modal visually constrains the user to the dialog window, keyboard users should have the same experience.</p> <p>To achieve this, we need to intercept two situations -</p> <ol> <li>If the user is tabbing <strong>forward</strong> (pressing the <code>TAB</code> key alone) from the <strong>last</strong> focusable element, then we need to move them to the <strong>first</strong> focusable element.</li> <li>If the user is tabbing <strong>backward</strong> (pressing the <code>SHIFT</code> and <code>TAB</code> keys) from the <strong>first</strong> focusable element, then we need to move them to the <strong>last</strong> focusable element.</li> </ol> <p>If there is only one focusable element, then just prevent any behaviour from occurring when the user presses the <code>TAB</code> key.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token class-name">Dialog</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">handleKeyDown</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><br> <span class="token keyword">var</span> Dialog <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span><br> <span class="token keyword">var</span> <span class="token constant">KEY_TAB</span> <span class="token operator">=</span> <span class="token number">9</span><span class="token punctuation">;</span><br><br> <span class="token keyword">function</span> <span class="token function">handleBackwardTab</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> document<span class="token punctuation">.</span>activeElement <span class="token operator">===</span> Dialog<span class="token punctuation">.</span>firstFocusableEl <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> Dialog<span class="token punctuation">.</span>lastFocusableEl<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> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><br> <span class="token keyword">function</span> <span class="token function">handleForwardTab</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> document<span class="token punctuation">.</span>activeElement <span class="token operator">===</span> Dialog<span class="token punctuation">.</span>lastFocusableEl <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> Dialog<span class="token punctuation">.</span>firstFocusableEl<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> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><br><br> <span class="token keyword">switch</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>keyCode<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">case</span> <span class="token constant">KEY_TAB</span><span class="token operator">:</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> Dialog<span class="token punctuation">.</span>focusableEls<span class="token punctuation">.</span>length <span class="token operator">===</span> <span class="token number">1</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</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><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> e<span class="token punctuation">.</span>shiftKey <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">handleBackwardTab</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 keyword">else</span> <span class="token punctuation">{</span><br> <span class="token function">handleForwardTab</span><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">break</span><span class="token punctuation">;</span><br> <span class="token keyword">default</span><span class="token operator">:</span><br> <span class="token keyword">break</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span> <span class="token comment">// end switch</span><br><br><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre> <h2 id="6-allow-the-esc-key-to-close-the-dialog" tabindex="-1">6. Allow the <code>ESC</code> Key to Close the Dialog <a class="header-anchor" href="https://bitsofco.de/accessible-modal-dialog/">#</a></h2> <blockquote> <p>When the dialog is open, pressing the <code>ESC</code> key should close it</p> </blockquote> <p>Besides our intercept of the <code>TAB</code> key, all other keys should work as they normally would. This means that pressing the <code>ESC</code> key should close anything that is open.</p> <p>We can modify the <code>Dialog.prototype.handleKeyDown</code> function to allow for this.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token class-name">Dialog</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">handleKeyDown</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><br> <span class="token keyword">var</span> Dialog <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">;</span><br> <span class="token keyword">var</span> <span class="token constant">KEY_TAB</span> <span class="token operator">=</span> <span class="token number">9</span><span class="token punctuation">;</span><br> <span class="token keyword">var</span> <span class="token constant">KEY_ESC</span> <span class="token operator">=</span> <span class="token number">27</span><span class="token punctuation">;</span><br><br> <span class="token keyword">switch</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>keyCode<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">case</span> <span class="token constant">KEY_TAB</span><span class="token operator">:</span><br> <span class="token comment">/* Handle if TAB key is pressed (see above) */</span><br> <span class="token keyword">case</span> <span class="token constant">KEY_ESC</span><span class="token operator">:</span><br> Dialog<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">break</span><span class="token punctuation">;</span><br> <span class="token keyword">default</span><span class="token operator">:</span><br> <span class="token keyword">break</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span> <span class="token comment">// end switch</span><br><br><span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre> <h2 id="demo-and-source-code" tabindex="-1">Demo &amp; Source Code <a class="header-anchor" href="https://bitsofco.de/accessible-modal-dialog/">#</a></h2> <ul> <li><a href="https://ireade.github.io/accessible-modal-dialog/">Live Demo</a></li> <li><a href="https://github.com/ireade/accessible-modal-dialog">Source Code</a></li> </ul> The Background Properties 2016-07-26T00:00:00Z https://bitsofco.de/the-background-properties/ <p>As I have mentioned before, <a href="https://bitsofco.de/controlling-the-box-model">every element in the document tree is just a rectangular box</a>. Each of these boxes has a background layer that can either be fully transparent, a colour, or an image. This background layer is controlled by 8 CSS properties (plus 1 shorthand property).</p> <h2 id="background-color" tabindex="-1">background-color <a class="header-anchor" href="https://bitsofco.de/the-background-properties/">#</a></h2> <p>The <code>background-color</code> property sets a background colour for the element. It's value can be any accepted colour value, or the <code>transparent</code> keyword.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.left</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 class="token selector">.middle</span> <span class="token punctuation">{</span> <span class="token property">background-color</span><span class="token punctuation">:</span> #67b3dd<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.right</span> <span class="token punctuation">{</span> <span class="token property">background-color</span><span class="token punctuation">:</span> transparent<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/kDY-z2oj5Z-799.avif 799w"><source type="image/webp" srcset="https://bitsofco.de/img/kDY-z2oj5Z-799.webp 799w"><img alt="Demo of the background-color property" loading="lazy" decoding="async" src="https://bitsofco.de/img/kDY-z2oj5Z-799.png" width="799" height="345"></picture></p> <p>The background colour is drawn within the area of the box model specified by the <a href="https://bitsofco.de/the-background-properties/"><code>background-clip</code></a> property. If any background images are also set, the colour layer is drawn behind them. Unlike image layers which can be multiple, we can have only one colour layer for an element.</p> <h2 id="background-image" tabindex="-1">background-image <a class="header-anchor" href="https://bitsofco.de/the-background-properties/">#</a></h2> <p>The <code>background-image</code> property defines a background image (or images) for the element. It's value is typically a url to an image defined with the <code>url()</code> notation. A value of <code>none</code> can also be used, which will count as a layer, but an empty one.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.left</span> <span class="token punctuation">{</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">'ire.png'</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.right</span> <span class="token punctuation">{</span> <span class="token property">background-image</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/FsfEI0C0SD-802.avif 802w"><source type="image/webp" srcset="https://bitsofco.de/img/FsfEI0C0SD-802.webp 802w"><img alt="Demo of the background-image property" loading="lazy" decoding="async" src="https://bitsofco.de/img/FsfEI0C0SD-802.png" width="802" height="344"></picture></p> <p>We can also specify multiple background images, each separated by a comma. Each subsequent image drawn is placed behind the previous on the z-axis.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.middle</span> <span class="token punctuation">{</span><br> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">'khaled.png'</span><span class="token punctuation">)</span></span><span class="token punctuation">,</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">'ire.png'</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span><br><br> <span class="token comment">/* Other styles */</span><br> <span class="token property">background-repeat</span><span class="token punctuation">:</span> no-repeat<span class="token punctuation">;</span><br> <span class="token property">background-size</span><span class="token punctuation">:</span> 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/92dsGGPTYx-792.avif 792w"><source type="image/webp" srcset="https://bitsofco.de/img/92dsGGPTYx-792.webp 792w"><img alt="Demo of the background-image property showing multiple backgrounds" loading="lazy" decoding="async" src="https://bitsofco.de/img/92dsGGPTYx-792.png" width="792" height="336"></picture></p> <h2 id="background-repeat" tabindex="-1">background-repeat <a class="header-anchor" href="https://bitsofco.de/the-background-properties/">#</a></h2> <p>The <code>background-repeat</code> property controls how a background image is tiled after it has been sized (by the <a href="https://bitsofco.de/the-background-properties/"><code>background-size</code></a> property) and positioned (by the <a href="https://bitsofco.de/the-background-properties/"><code>background-position</code></a> property).</p> <p>The value for this property can be one of the following keywords - <code>repeat-x</code>, <code>repeat-y</code>, <code>repeat</code>, <code>space</code>, <code>round</code>, <code>no-repeat</code>. Besides the first two (<code>repeat-x</code> and <code>repeat-y</code>), the other values can be defined either once for both the x-axis and y-axis, or each dimension individually.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.top-outer-left</span> <span class="token punctuation">{</span> <span class="token property">background-repeat</span><span class="token punctuation">:</span> repeat-x<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.top-inner-left</span> <span class="token punctuation">{</span> <span class="token property">background-repeat</span><span class="token punctuation">:</span> repeat-y<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.top-inner-right</span> <span class="token punctuation">{</span> <span class="token property">background-repeat</span><span class="token punctuation">:</span> repeat<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.top-outer-right</span> <span class="token punctuation">{</span> <span class="token property">background-repeat</span><span class="token punctuation">:</span> space<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><br><span class="token selector">.bottom-outer-left</span> <span class="token punctuation">{</span> <span class="token property">background-repeat</span><span class="token punctuation">:</span> round<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.bottom-inner-left</span> <span class="token punctuation">{</span> <span class="token property">background-repeat</span><span class="token punctuation">:</span> no-repeat<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.bottom-inner-right</span> <span class="token punctuation">{</span> <span class="token property">background-repeat</span><span class="token punctuation">:</span> space repeat<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.bottom-outer-right</span> <span class="token punctuation">{</span> <span class="token property">background-repeat</span><span class="token punctuation">:</span> round space<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/FgqSt1SKPq-809.avif 809w"><source type="image/webp" srcset="https://bitsofco.de/img/FgqSt1SKPq-809.webp 809w"><img alt="Demo of the background-repeat property" loading="lazy" decoding="async" src="https://bitsofco.de/img/FgqSt1SKPq-809.png" width="809" height="580"></picture></p> <h2 id="background-size" tabindex="-1">background-size <a class="header-anchor" href="https://bitsofco.de/the-background-properties/">#</a></h2> <p>The <code>background-size</code> property defines the size of the background image. It's value can either be a keyword, a length, or a percentage.</p> <p>The keywords available for this property are <code>contain</code> and <code>cover</code>. The keyword <code>contain</code> will scale the image to the largest size it can be where both it's height and width can fit within the area. <code>cover</code>, on the other hand, will scale the image to the smallest possible size where entire background area is still covered.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.left</span> <span class="token punctuation">{</span><br> <span class="token property">background-size</span><span class="token punctuation">:</span> contain<span class="token punctuation">;</span><br> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">'ire.png'</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span><br> <span class="token property">background-repeat</span><span class="token punctuation">:</span> no-repeat<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><span class="token selector">.right</span> <span class="token punctuation">{</span> <span class="token property">background-size</span><span class="token punctuation">:</span> cover<span class="token punctuation">;</span> <span class="token comment">/* Other styles same as .left */</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/YRBnggho0B-805.avif 805w"><source type="image/webp" srcset="https://bitsofco.de/img/YRBnggho0B-805.webp 805w"><img alt="Demo of the background-size property with keywords" loading="lazy" decoding="async" src="https://bitsofco.de/img/YRBnggho0B-805.png" width="805" height="345"></picture></p> <p>For length values and percentage values, we can specify both the width and height of the background image. Percentage values are calculated in relation to the size of the element.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.left</span> <span class="token punctuation">{</span> <span class="token property">background-size</span><span class="token punctuation">:</span> 50px<span class="token punctuation">;</span> <span class="token comment">/* Other styles same as .left */</span> <span class="token punctuation">}</span><br><span class="token selector">.right</span> <span class="token punctuation">{</span> <span class="token property">background-size</span><span class="token punctuation">:</span> 50% 80%<span class="token punctuation">;</span> <span class="token comment">/* Other styles same as .left */</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/BfQIyZ2_J8-809.avif 809w"><source type="image/webp" srcset="https://bitsofco.de/img/BfQIyZ2_J8-809.webp 809w"><img alt="Demo of the background-size property with length values" loading="lazy" decoding="async" src="https://bitsofco.de/img/BfQIyZ2_J8-809.png" width="809" height="341"></picture></p> <h2 id="background-attachment" tabindex="-1">background-attachment <a class="header-anchor" href="https://bitsofco.de/the-background-properties/">#</a></h2> <p>The <code>background-attachment</code> property controls how the background image scrolls in relation to the viewport and the element. It has three potential values.</p> <p>The keyword <code>fixed</code> means that the background image is fixed to the viewport and does not move, even when the user is scrolling along the viewport. <code>local</code> means that the background should be fixed to its position in the element. If the element has a scrolling mechanism and the background image is positioned to the top, as the user scrolls down the element, the background image will scroll out of view. Finally, <code>scroll</code> means that the background image is fixed and will not scroll even with the contents of it's element.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.left</span> <span class="token punctuation">{</span><br> <span class="token property">background-attachment</span><span class="token punctuation">:</span> fixed<span class="token punctuation">;</span><br> <span class="token property">background-size</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">'ire.png'</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span><br> <span class="token property">background-repeat</span><span class="token punctuation">:</span> no-repeat<span class="token punctuation">;</span><br> <span class="token property">overflow</span><span class="token punctuation">:</span> scroll<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><span class="token selector">.middle</span> <span class="token punctuation">{</span> <span class="token property">background-attachment</span><span class="token punctuation">:</span> local<span class="token punctuation">;</span> <span class="token comment">/* Other styles same as .left */</span> <span class="token punctuation">}</span><br><span class="token selector">.right</span> <span class="token punctuation">{</span> <span class="token property">background-attachment</span><span class="token punctuation">:</span> scroll<span class="token punctuation">;</span> <span class="token comment">/* Other styles same as .left */</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/HrmY5Su2z5-798.avif 798w"><source type="image/gif" srcset="https://bitsofco.de/img/HrmY5Su2z5-798.gif 798w"><img alt="Demo of the background-attachment property" loading="lazy" decoding="async" src="https://bitsofco.de/img/HrmY5Su2z5-798.webp" width="798" height="346"></picture></p> <h2 id="background-position" tabindex="-1">background-position <a class="header-anchor" href="https://bitsofco.de/the-background-properties/">#</a></h2> <p>This property, in combination with the <a href="https://bitsofco.de/the-background-properties/"><code>background-origin</code></a> property, defines where the starting position for the background image should be. It's value can either be a keyword, a length, or a percentage, and we can specify the position along the x-axis as well as the y-axis.</p> <p>The keywords available are <code>top</code>, <code>right</code>, <code>bottom</code>, <code>left</code>, and <code>center</code>. We can use these keywords in any combination, and if only one keyword is specified, the other is presumed to be <code>center</code>.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.top-left</span> <span class="token punctuation">{</span><br> <span class="token property">background-position</span><span class="token punctuation">:</span> top<span class="token punctuation">;</span><br> <span class="token property">background-size</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">'ire.png'</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span><br> <span class="token property">background-repeat</span><span class="token punctuation">:</span> no-repeat<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><span class="token selector">.top-middle</span> <span class="token punctuation">{</span> <span class="token property">background-position</span><span class="token punctuation">:</span> right<span class="token punctuation">;</span> <span class="token comment">/* Other styles same as .top-left */</span> <span class="token punctuation">}</span><br><span class="token selector">.top-right</span> <span class="token punctuation">{</span> <span class="token property">background-position</span><span class="token punctuation">:</span> bottom<span class="token punctuation">;</span> <span class="token comment">/* Other styles same as .top-left */</span> <span class="token punctuation">}</span><br><span class="token selector">.bottom-left</span> <span class="token punctuation">{</span> <span class="token property">background-position</span><span class="token punctuation">:</span> left<span class="token punctuation">;</span> <span class="token comment">/* Other styles same as .top-left */</span> <span class="token punctuation">}</span><br><span class="token selector">.bottom-right</span> <span class="token punctuation">{</span> <span class="token property">background-position</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span> <span class="token comment">/* Other styles same as .top-left */</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/nbC23b1pdk-796.avif 796w"><source type="image/webp" srcset="https://bitsofco.de/img/nbC23b1pdk-796.webp 796w"><img alt="Demo of the background-position property with keywords" loading="lazy" decoding="async" src="https://bitsofco.de/img/nbC23b1pdk-796.png" width="796" height="562"></picture></p> <p>For length and percentage values, we can also specify the position along the x-axis and y-axis. Percentage values are in relation to the containing element.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.left</span> <span class="token punctuation">{</span> <span class="token property">background-position</span><span class="token punctuation">:</span> 20px 70px<span class="token punctuation">;</span> <span class="token comment">/* Others same as .top-left */</span> <span class="token punctuation">}</span><br><span class="token selector">.right</span> <span class="token punctuation">{</span> <span class="token property">background-position</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span> <span class="token comment">/* Others same as .top-left */</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/3YOVMD83AZ-799.avif 799w"><source type="image/webp" srcset="https://bitsofco.de/img/3YOVMD83AZ-799.webp 799w"><img alt="Demo of the background-position property with length values" loading="lazy" decoding="async" src="https://bitsofco.de/img/3YOVMD83AZ-799.png" width="799" height="339"></picture></p> <h2 id="background-origin" tabindex="-1">background-origin <a class="header-anchor" href="https://bitsofco.de/the-background-properties/">#</a></h2> <p>The <code>background-origin</code> property specifies which area of <a href="https://bitsofco.de/controlling-the-box-model">the box model</a> the background image should be positioned according to.</p> <p>The values available are <code>border-box</code>, which positions the image based on the Border Area, <code>padding-box</code>, which uses the Padding Area, and <code>content-box</code>, which uses the Content Area.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.left</span> <span class="token punctuation">{</span><br> <span class="token property">background-origin</span><span class="token punctuation">:</span> border-box<span class="token punctuation">;</span><br> <span class="token property">background-size</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">'ire.png'</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span><br> <span class="token property">background-repeat</span><span class="token punctuation">:</span> no-repeat<span class="token punctuation">;</span><br> <span class="token property">background-position</span><span class="token punctuation">:</span> top left<span class="token punctuation">;</span><br> <span class="token property">border</span><span class="token punctuation">:</span> 10px dotted black<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><span class="token selector">.middle</span> <span class="token punctuation">{</span> <span class="token property">background-origin</span><span class="token punctuation">:</span> padding-box<span class="token punctuation">;</span> <span class="token comment">/* Other styles same as .left*/</span> <span class="token punctuation">}</span><br><span class="token selector">.right</span> <span class="token punctuation">{</span> <span class="token property">background-origin</span><span class="token punctuation">:</span> content-box<span class="token punctuation">;</span> <span class="token comment">/* Other styles same as .left*/</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/t96z47kKqD-802.avif 802w"><source type="image/webp" srcset="https://bitsofco.de/img/t96z47kKqD-802.webp 802w"><img alt="Demo of the background-origin property" loading="lazy" decoding="async" src="https://bitsofco.de/img/t96z47kKqD-802.png" width="802" height="341"></picture></p> <h2 id="background-clip" tabindex="-1">background-clip <a class="header-anchor" href="https://bitsofco.de/the-background-properties/">#</a></h2> <p>The <code>background-clip</code> property determines the background painting area, which is the area that the background can be painted onto. Like the <a href="https://bitsofco.de/the-background-properties/"><code>background-origin</code></a> property, it is based on the box model areas.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.left</span><span class="token punctuation">{</span><br> <span class="token property">background-clip</span><span class="token punctuation">:</span> border-box<span class="token punctuation">;</span><br> <span class="token property">background-size</span><span class="token punctuation">:</span> 50%<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">background-repeat</span><span class="token punctuation">:</span> no-repeat<span class="token punctuation">;</span><br> <span class="token property">background-position</span><span class="token punctuation">:</span> top left<span class="token punctuation">;</span><br> <span class="token property">border</span><span class="token punctuation">:</span> 10px dotted black<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><span class="token selector">.middle</span> <span class="token punctuation">{</span> <span class="token property">background-clip</span><span class="token punctuation">:</span> padding-box<span class="token punctuation">;</span> <span class="token comment">/* Other styles same as .left*/</span> <span class="token punctuation">}</span><br><span class="token selector">.right</span> <span class="token punctuation">{</span> <span class="token property">background-clip</span><span class="token punctuation">:</span> content-box<span class="token punctuation">;</span> <span class="token comment">/* Other styles same as .left*/</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/GtKHqE-PKy-803.avif 803w"><source type="image/webp" srcset="https://bitsofco.de/img/GtKHqE-PKy-803.webp 803w"><img alt="Demo of the background-clip property" loading="lazy" decoding="async" src="https://bitsofco.de/img/GtKHqE-PKy-803.png" width="803" height="334"></picture></p> <h2 id="background" tabindex="-1">background <a class="header-anchor" href="https://bitsofco.de/the-background-properties/">#</a></h2> <p>Finally, the <code>background</code> property is a shorthand for the other background related properties. The order of the sub-properties does not matter because the data types for each property are different . However, for the <code>background-origin</code> and <code>background-clip</code> properties, if only one box model area is specified, it is used for both properties. If two are specified, the first sets the <code>background-origin</code> property.</p> The Service Worker Lifecycle 2016-07-19T00:00:00Z https://bitsofco.de/the-service-worker-lifecycle/ <p>If you have worked with Service Workers, you may have run into some issues with previous Service Workers still being in control of a document, even though the file itself has been updated. The reason for this is to do with some nuances in the lifecycle of the Service Worker; it may be installed and valid, but not yet actually in control of the document.</p> <p>A Service Worker can be in one of the following six states - <strong>parsed</strong>, <strong>installing</strong>, <strong>installed</strong>, <strong>activating</strong>, <strong>activated</strong>, and <strong>redundant</strong>.</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> <h2 id="parsed" tabindex="-1">Parsed <a class="header-anchor" href="https://bitsofco.de/the-service-worker-lifecycle/">#</a></h2> <p>When we first attempt to register a Service Worker, the user agent parses the script and obtains the entry point. If parsing is successful (and some other requirements, e.g. HTTPS, are met), we will have access to the Service Worker registration object. This contains information about the state of the Service Worker as well as it’s scope.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* In main.js */</span><br><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token string">'serviceWorker'</span> <span class="token keyword">in</span> navigator<span class="token punctuation">)</span> <span class="token punctuation">{</span><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">'./sw.js'</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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">registration</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">"Service Worker Registered"</span><span class="token punctuation">,</span> registration<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 function">catch</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err</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">"Service Worker Failed to Register"</span><span class="token punctuation">,</span> err<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></code></pre> <p>Successful registration of the Service Worker doesn’t mean that it has been installed yet or is active, just that the script has been successfully parsed, it is on the same origin as the document, and the origin is HTTPS. Once this is complete, the Service Worker moves on to the next state.</p> <h2 id="installing" tabindex="-1">Installing <a class="header-anchor" href="https://bitsofco.de/the-service-worker-lifecycle/">#</a></h2> <p>Once the Service Worker script has been parsed, the user agent attempts to install it and it moves to the <strong>installing</strong> state. In the Service Worker <code>registration</code> object, we can check for this state in the <code>installing</code> child object.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* In main.js */</span><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">'./sw.js'</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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">registration</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>registration<span class="token punctuation">.</span>installing<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Service Worker is Installing</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p>During the installing state, the <code>install</code> event in the Service Worker script is carried out. In a typical install event, we cache the static files for the document.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* In sw.js */</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> event<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>currentCacheName<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">cache</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> cache<span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>arrayOfFilesToCache<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><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>If there is an <code>event.waitUntil()</code> method in the event, the installing event will not be successful until the Promise within it is resolved. If the Promise is rejected, the install event fails and the Service Worker becomes <strong>redundant</strong>.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* In sw.js */</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> event<span class="token punctuation">.</span><span class="token function">waitUntil</span><span class="token punctuation">(</span><br> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">reject</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Failure</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>Install Event will fail</p> <h2 id="installed-waiting" tabindex="-1">Installed / Waiting <a class="header-anchor" href="https://bitsofco.de/the-service-worker-lifecycle/">#</a></h2> <p>If the installation is successful, the Service Worker moves to the <strong>installed</strong> (also called <strong>waiting</strong>) state. In this state it is a valid, but not yet active, worker. It is not yet in control of the document, but rather is waiting to take control from the current worker.</p> <p>In the Service Worker <code>registration</code> object, we can check for this state in the <code>waiting</code> child object.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* In main.js */</span><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">'./sw.js'</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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">registration</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>registration<span class="token punctuation">.</span>waiting<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Service Worker is Waiting</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p>This can be a good time to notify the app user that they can update to a new version, or automatically update for them.</p> <h2 id="activating" tabindex="-1">Activating <a class="header-anchor" href="https://bitsofco.de/the-service-worker-lifecycle/">#</a></h2> <p>The <strong>activating</strong> state will be triggered for a waiting Service Worker in one of the following scenarios -</p> <ul> <li>If there is no current active worker already</li> <li>If the <code>self.skipWaiting()</code> method is called in the Service Worker script</li> <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>During the activating state, the <code>activate</code> event in the Service Worker script is carried out. In a typical activate event, we clear files from old caches.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* In sw.js */</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> event<span class="token punctuation">.</span><span class="token function">waitUntil</span><span class="token punctuation">(</span><br> <span class="token comment">// Get all the cache names</span><br> caches<span class="token punctuation">.</span><span class="token function">keys</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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">cacheNames</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><br> <span class="token comment">// Get all the items that are stored under a different cache name than the current one</span><br> cacheNames<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">cacheName</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> cacheName <span class="token operator">!=</span> currentCacheName<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">map</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">cacheName</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Delete the items</span><br> <span class="token keyword">return</span> caches<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span>cacheName<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 comment">// end Promise.all()</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// end caches.keys()</span><br> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// end event.waitUntil()</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>Similarly to the install event, if there is an <code>event.waitUntil()</code> method in the activate event, activation will not be successful until the Promise is resolved. If the Promise is rejected, the activate event fails and the Service Worker becomes <strong>redundant</strong>.</p> <h2 id="activated" tabindex="-1">Activated <a class="header-anchor" href="https://bitsofco.de/the-service-worker-lifecycle/">#</a></h2> <p>If activation is successful, the Service Worker moves to the <strong>active</strong> state. In this state, it is an active worker in full control of the document. In the Service Worker <code>registration</code> object, we can check for this state in the <code>active</code> child object.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* In main.js */</span><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">'./sw.js'</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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">registration</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>registration<span class="token punctuation">.</span>active<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Service Worker is Active</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p>When a Service Worker is active, it can now handle the functional events - <code>fetch</code>, and <code>message</code>.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* In sw.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">// Do stuff with fetch events</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><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">'message'</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">// Do stuff with postMessages received from document</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <h2 id="redundant" tabindex="-1">Redundant <a class="header-anchor" href="https://bitsofco.de/the-service-worker-lifecycle/">#</a></h2> <p>A Service Worker can become <strong>redundant</strong> for one of the following reasons -</p> <ul> <li>If the installing event failed</li> <li>If the activating event failed</li> <li>If a new Service Worker replaces it as the active service worker</li> </ul> <p>If the Service Worker is redundant for the first two reasons, we can see it (and related information) in our Dev Tools -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/HqicOWuuZ3-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/HqicOWuuZ3-780.webp 780w"><img alt="Service Worker Redundant in DevTools" loading="lazy" decoding="async" src="https://bitsofco.de/img/HqicOWuuZ3-780.png" width="780" height="357"></picture></p> <p>If there was a previously active Service Worker, it maintains control of the document.</p> JavaScript Promises 101 2016-07-12T00:00:00Z https://bitsofco.de/javascript-promises-101/ <p>A JavaScript Promise represents the result of an operation that hasn't been completed yet, but will at some undetermined point in the future. An example of such an operation is a network request. When we fetch data from some source, for example an API, there is no way for us to absolutely determine when the response will be received.</p> <p>This can be problematic if we have other operations dependent on the completion of this network request. Without Promises, we would have to use callbacks to deal with actions that need to happen in sequence. This isn't necessarily a problem if we only have one asynchronous action. But if we need to complete multiple asynchronous steps in sequence, callbacks become unmanageable and result in the infamous <a href="https://callbackhell.com/">callback hell</a>.</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">responseOne</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">doSomethingElse</span><span class="token punctuation">(</span>responseOne<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">responseTwo<span class="token punctuation">,</span> err</span><span class="token punctuation">)</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 punctuation">{</span> <span class="token function">handleError</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><br> <span class="token function">doMoreStuff</span><span class="token punctuation">(</span>responseTwo<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">responseThree<span class="token punctuation">,</span> err</span><span class="token punctuation">)</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 punctuation">{</span> <span class="token function">handleAnotherError</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><br> <span class="token function">doFinalThing</span><span class="token punctuation">(</span>responseThree<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</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 punctuation">{</span> <span class="token function">handleAnotherError</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><br> <span class="token comment">// Complete</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// end doFinalThing</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// end doMoreStuff</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// end doSomethingElse</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// end doSomething</span></code></pre> <p>Promises provide a standardised and cleaner method of dealing with tasks that need to happen in sequence.</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token function">doSomething</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>doSomethingElse<span class="token punctuation">)</span><br><span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span>handleError<span class="token punctuation">)</span><br><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>doMoreStuff<span class="token punctuation">)</span><br><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>doFinalThing<span class="token punctuation">)</span><br><span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span>handleAnotherError<span class="token punctuation">)</span></code></pre> <h2 id="creating-promises" tabindex="-1">Creating Promises <a class="header-anchor" href="https://bitsofco.de/javascript-promises-101/">#</a></h2> <p>A Promise is created using the Promise Constructor. This accepts a function with two arguments (<code>resolve</code> &amp; <code>reject</code>) as its only parameter.</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token keyword">var</span> promise <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span> <span class="token keyword">function</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 punctuation">{</span> <span class="token comment">/* Promise content */</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/fq7-fGDyX9-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/fq7-fGDyX9-780.webp 780w"><img alt="Creating Promises" loading="lazy" decoding="async" src="https://bitsofco.de/img/fq7-fGDyX9-780.png" width="780" height="278"></picture></p> <p>Within the function, we can execute whatever asynchronous task we want. To mark the promise as <strong>fulfilled</strong>, we call <code>resolve()</code>, optionally passing a value we want to return. To mark the promise as <strong>rejected</strong> or failed, we call <code>reject()</code>, optionally passing an error message. Before a promise is fulfilled or rejected, it is in a <strong>pending</strong> state.</p> <p>Here is a common promise-ified version of an XMLHttpRequest -</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token comment">/* CREDIT - Jake Archibald, https://www.html5rocks.com/en/tutorials/es6/promises/ */</span><br><br><span class="token keyword">function</span> <span class="token function">get</span><span class="token punctuation">(</span><span class="token parameter">url</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 keyword">function</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 punctuation">{</span><br><br> <span class="token keyword">var</span> req <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">XMLHttpRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> req<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span><span class="token string">'GET'</span><span class="token punctuation">,</span> url<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> req<span class="token punctuation">.</span><span class="token function-variable function">onload</span> <span class="token operator">=</span> <span class="token keyword">function</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>req<span class="token punctuation">.</span>status <span class="token operator">==</span> <span class="token number">200</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">resolve</span><span class="token punctuation">(</span>req<span class="token punctuation">.</span>response<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* PROMISE RESOLVED */</span><br> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br> <span class="token function">reject</span><span class="token punctuation">(</span><span class="token function">Error</span><span class="token punctuation">(</span>req<span class="token punctuation">.</span>statusText<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* PROMISE REJECTED */</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><span class="token punctuation">;</span><br><br> req<span class="token punctuation">.</span><span class="token function-variable function">onerror</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">reject</span><span class="token punctuation">(</span><span class="token function">Error</span><span class="token punctuation">(</span><span class="token string">"Network Error"</span><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><br> req<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><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="using-promises" tabindex="-1">Using Promises <a class="header-anchor" href="https://bitsofco.de/javascript-promises-101/">#</a></h2> <p>Once we have created the Promise, we need to actually use it. To execute the promise-ified function, we can call it like any regular function. But, because it is a promise, we now have access to the <code>.then()</code> method, which we can append to the function and which will be executed when the Promise is no longer pending.</p> <p>The <code>.then()</code>method accepts two optional parameters. First, a function called if the promise is fulfilled. Second, a function called if the promise is rejected.</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token function">get</span><span class="token punctuation">(</span>url<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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">/* successFunction */</span><br><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">/* errorFunction */</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/3xn9Klxc5Y-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/3xn9Klxc5Y-780.webp 780w"><img alt="Using Promises" loading="lazy" decoding="async" src="https://bitsofco.de/img/3xn9Klxc5Y-780.png" width="780" height="338"></picture></p> <h2 id="handling-errors" tabindex="-1">Handling Errors <a class="header-anchor" href="https://bitsofco.de/javascript-promises-101/">#</a></h2> <p>Since both the success and error functions are optional, we can split them into two <code>.then()</code>s for better readability.</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token function">get</span><span class="token punctuation">(</span>url<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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">/* successFunction */</span><br><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">undefined</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 keyword">undefined</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">/* errorFunction */</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p>To make things even more readable, we make use of the <code>.catch()</code> method, which is a shorthand for a <code>.then(undefined, errorFunction)</code>.</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token function">get</span><span class="token punctuation">(</span>url<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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">/* successFunction */</span><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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">/* errorFunction */</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Oz4nsQrgDB-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/Oz4nsQrgDB-780.webp 780w"><img alt="Handling Errors" loading="lazy" decoding="async" src="https://bitsofco.de/img/Oz4nsQrgDB-780.png" width="780" height="294"></picture></p> <h2 id="chaining" tabindex="-1">Chaining <a class="header-anchor" href="https://bitsofco.de/javascript-promises-101/">#</a></h2> <p>The real value in promises is when we have multiple asynchronous functions we need to execute in order. We can chain <code>.then()</code> and <code>.catch()</code> together to create a sequence of asynchronous functions.</p> <p>We do this by returning another promise within a success or error function. For example -</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token function">get</span><span class="token punctuation">(</span>url<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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> response <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>response<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">var</span> secondURL <span class="token operator">=</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>url<br> <span class="token keyword">return</span> <span class="token function">get</span><span class="token punctuation">(</span> secondURL <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Return another Promise */</span><br><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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> response <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>response<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">var</span> thirdURL <span class="token operator">=</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>url<br> <span class="token keyword">return</span> <span class="token function">get</span><span class="token punctuation">(</span> thirdURL <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* Return another Promise */</span><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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">handleError</span><span class="token punctuation">(</span>err<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>If a promise within the chain resolved, it will move on to the next success function (<code>.then()</code>) in the sequence. If, on the other hand, a promise rejected, it will jump to the next error function (<code>.catch()</code>) in the sequence.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/ZKds3VW43s-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/ZKds3VW43s-780.webp 780w"><img alt="" loading="lazy" decoding="async" src="https://bitsofco.de/img/ZKds3VW43s-780.png" width="780" height="400"></picture></p> <h2 id="executing-promises-in-parallel" tabindex="-1">Executing Promises in Parallel <a class="header-anchor" href="https://bitsofco.de/javascript-promises-101/">#</a></h2> <p>There may be cases where we want to execute a bunch of promise-ified functions in parallel, and then perform an action only when all the promises have been fulfilled. For example, if we want to fetch a bunch of images and display them on the page.</p> <p>To do this, we need to make use of two methods. First, the <code>Array.map()</code> method allows us to perform an action on each item in an array, and creates a new array of the results of these actions.</p> <p>Second, the <code>Promise.all()</code> method returns a promise that is only resolved when every promise within an array is resolved. If any single promise within the array is rejected, the <code>Promise.all()</code> promise is also rejected.</p> <pre class="language-javascript" tabindex="0"><code class="language-javascript"><span class="token keyword">var</span> arrayOfURLs <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'one.json'</span><span class="token punctuation">,</span> <span class="token string">'two.json'</span><span class="token punctuation">,</span> <span class="token string">'three.json'</span><span class="token punctuation">,</span> <span class="token string">'four.json'</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br><span class="token keyword">var</span> arrayOfPromises <span class="token operator">=</span> arrayOfURLs<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>get<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>Promise<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span>arrayOfPromises<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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">arrayOfResults</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">/* Do something when all Promises are resolved */</span><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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">/* Handle error is any of Promises fails */</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/tTdp9OOiPn-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/tTdp9OOiPn-780.webp 780w"><img alt="Executing Promises in Parallel" loading="lazy" decoding="async" src="https://bitsofco.de/img/tTdp9OOiPn-780.png" width="780" height="400"></picture></p> <p>If we look at the Network panel in our Development tools, we can see that all the fetch requests are happening in parallel.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Ii1QoHCEP5-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/Ii1QoHCEP5-780.webp 780w"><img alt="Network requests in parallel" loading="lazy" decoding="async" src="https://bitsofco.de/img/Ii1QoHCEP5-780.png" width="780" height="315"></picture></p> <h2 id="support" tabindex="-1">Support <a class="header-anchor" href="https://bitsofco.de/javascript-promises-101/">#</a></h2> <p class="ciu_embed" data-feature="promises" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false"> <picture> <source type="image/webp" srcset="https://caniuse.bitsofco.de/image/promises.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/promises.png"> <img src="https://caniuse.bitsofco.de/image/promises.jpg" alt="Data on support for the promises feature across the major browsers from caniuse.com"> </picture> </p> <p>If you have to support Internet Explorer or Opera Mini, you can make use of this <a href="https://github.com/taylorhakes/promise-polyfill">Promise Polyfill</a>.</p> The :target Trick 2016-07-05T00:00:00Z https://bitsofco.de/the-target-trick/ <p>The <code>:target</code> pseudo-class refers to an element within the document that the URL’s fragment points to. For example, <mark>this piece of text</mark> is wrapped in a <code>&lt;mark&gt;</code> element which has an ID of <code>#target-test</code>. If you went to the url, <strong>GHOST_URL</strong>/the-target-trick#target-test, then that element will be the target and and styles applied to the <code>:target</code> pseudo-class for that element will take effect.</p> <p>Last year, I wrote about the <code>:target</code> pseudo-class in my article on <a href="https://bitsofco.de/5-lesser-used-css-selectors">5 Lesser Used CSS Selectors</a> (and some use cases for them). The example I mentioned for the <code>:target</code> pseudo-class was to highlight the section of the page that is being referenced. For example, adding a background colour or border to it like in the example above.</p> <p>But it recently occurred to me that we can use the <code>:target</code> pseudo-element in a much more useful way, to create interactive elements on the page without needing javascript.</p> <h2 id="example-1-hiding-and-showing-content" tabindex="-1">Example 1 - Hiding &amp; Showing Content <a class="header-anchor" href="https://bitsofco.de/the-target-trick/">#</a></h2> <p>A simple use case for the <code>:target</code> pseudo-class is just to hide and show the content targeted. In a blog, for example, we may not want to display the comments section until the user clicks to show them. To achieve this we can simply hide the element unless it is a <code>:target</code>.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>#comments<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Show Comments<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>comments<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">&lt;</span>h3</span><span class="token punctuation">></span></span>Comments<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">></span></span><br> <span class="token comment">&lt;!-- Comments here... --></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>Hide Comments<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span></code></pre> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">#comments:not(:target)</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><span class="token selector">#comments:target</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><picture><source type="image/avif" srcset="https://bitsofco.de/img/cBCo2FycLY-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/cBCo2FycLY-720.gif 720w"><img alt="Demo of Hiding &amp; Showing Content using :target" loading="lazy" decoding="async" src="https://bitsofco.de/img/cBCo2FycLY-720.webp" width="720" height="472"></picture></p> <p><a href="https://demo.bitsofco.de/the-target-trick/hide-show.html">View Live Demo</a></p> <h2 id="example-2-a-slide-out-navigation-drawer" tabindex="-1">Example 2 - A Slide-Out Navigation Drawer <a class="header-anchor" href="https://bitsofco.de/the-target-trick/">#</a></h2> <p>Another use case is in creating a slide-out navigation drawer. We can fix the navigation drawer relative to the viewport to ensure that there will be no jumping around when the user clicks to open.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">#nav</span> <span class="token punctuation">{</span><br> <span class="token property">position</span><span class="token punctuation">:</span> fixed<span class="token punctuation">;</span><br> <span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 80%<span class="token punctuation">;</span><br> <span class="token property">max-width</span><span class="token punctuation">:</span> 400px<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">#nav:not(:target)</span> <span class="token punctuation">{</span><br> <span class="token property">right</span><span class="token punctuation">:</span> -100%<span class="token punctuation">;</span><br> <span class="token property">transition</span><span class="token punctuation">:</span> right 1.5s<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">#nav:target</span> <span class="token punctuation">{</span><br> <span class="token property">right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">transition</span><span class="token punctuation">:</span> right 1s<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/XFjSvALwwm-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/XFjSvALwwm-720.gif 720w"><img alt="Demo of A Slide-Out Navigation Drawer using :target" loading="lazy" decoding="async" src="https://bitsofco.de/img/XFjSvALwwm-720.webp" width="720" height="472"></picture></p> <p><a href="https://demo.bitsofco.de/the-target-trick/slide-out.html">View Live Demo</a></p> <h2 id="example-3-a-pop-up-modal" tabindex="-1">Example 3 - A Pop-Up Modal <a class="header-anchor" href="https://bitsofco.de/the-target-trick/">#</a></h2> <p>Taking this further, we can create a modal that will fill the entire page.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">#modal-container</span> <span class="token punctuation">{</span><br> <span class="token property">position</span><span class="token punctuation">:</span> fixed<span class="token punctuation">;</span><br> <span class="token property">top</span><span class="token punctuation">:</span> 0<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">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">background</span><span class="token punctuation">:</span> <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.8<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span><br> <span class="token property">justify-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span><br> <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">.modal</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 70%<span class="token punctuation">;</span><br> <span class="token property">background</span><span class="token punctuation">:</span> #fff<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 property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">#modal-container:not(:target)</span> <span class="token punctuation">{</span><br> <span class="token property">opacity</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">visibility</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span><br> <span class="token property">transition</span><span class="token punctuation">:</span> opacity 1s<span class="token punctuation">,</span> visibility 1s<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">#modal-container:target</span> <span class="token punctuation">{</span><br> <span class="token property">opacity</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span><br> <span class="token property">visibility</span><span class="token punctuation">:</span> visible<span class="token punctuation">;</span><br> <span class="token property">transition</span><span class="token punctuation">:</span> opacity 1s<span class="token punctuation">,</span> visibility 1s<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/MvQ3VOeVeZ-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/MvQ3VOeVeZ-720.gif 720w"><img alt="Demo of A Pop-Up Modal using :target" loading="lazy" decoding="async" src="https://bitsofco.de/img/MvQ3VOeVeZ-720.webp" width="720" height="472"></picture></p> <p><a href="https://demo.bitsofco.de/the-target-trick/modal.html">View Live Demo</a></p> <h2 id="example-4-changing-global-styles" tabindex="-1">Example 4 - Changing Global Styles <a class="header-anchor" href="https://bitsofco.de/the-target-trick/">#</a></h2> <p>A final use case, although a less semantically-proper one, could be to apply <code>:target</code> to the <code>&lt;body&gt;</code> element itself, and completely restyle the page or alter its layout.</p> <pre class="language-scss" tabindex="0"><code class="language-scss">#<span class="token property">body</span><span class="token punctuation">:</span><span class="token function">not</span><span class="token punctuation">(</span><span class="token punctuation">:</span>target<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token selector">main </span><span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 60%<span class="token punctuation">;</span> <span class="token punctuation">}</span><br> <span class="token selector">aside </span><span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 30%<span class="token punctuation">;</span> <span class="token punctuation">}</span><br> <span class="token selector">.show-sidebar-link </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><br><br><span class="token selector">#body:target </span><span class="token punctuation">{</span><br> <span class="token selector">main </span><span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token punctuation">}</span><br> <span class="token selector">aside </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span><br> <span class="token selector">.hide-sidebar-link </span><span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/XoPjUHjnwI-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/XoPjUHjnwI-720.gif 720w"><img alt="Demo of Changing Global Styles using :target" loading="lazy" decoding="async" src="https://bitsofco.de/img/XoPjUHjnwI-720.webp" width="720" height="472"></picture></p> <p><a href="https://demo.bitsofco.de/the-target-trick/body.html">View Live Demo</a></p> <h2 id="is-this-semantic-and-accessible" tabindex="-1">Is this Semantic and Accessible? <a class="header-anchor" href="https://bitsofco.de/the-target-trick/">#</a></h2> <p>As I mentioned in <a href="https://bitsofco.de/anchors-vs-buttons">Anchors vs Buttons</a>, when an <code>&lt;a&gt;</code> element is used, the browser expects that the user be navigated to a different page or a different section within the page. In these examples (except the last one), that is essentially what is happening. The only trick is that the target element is being styled so it is hidden and shown dynamically.</p> <p>As far as I can tell, there are two potential issues with this method -</p> <ol> <li>There is a change in the URL, which affects the user's browsing history. This means that if the user goes &quot;back&quot;, they may be unintentionally navigated to the target element.</li> <li>To &quot;close&quot; the target element, the user needs to be navigated to either another element or just a <code>#</code>. The latter, which I have used in my examples, is not the most semantic and will cause the user to jump to the top of the page if they aren't there already.</li> </ol> <p>Nonetheless, if used correctly, I think this method can still be at least a fallback to provide interaction for users if JavaScript is not an option. In some cases, like in my first example, it can even be preferable to and simpler than using JavaScript. As always, it depends on the scenario.</p> All the Generic CSS Data Types 2016-06-28T00:00:00Z https://bitsofco.de/generic-css-data-types/ <p>Values for properties in CSS can be in a number of formats. In order for a user agent to be able to determine if a value is valid, it needs to make sure it conforms to one of the speicfic types of values. These are called data types, and are typically written in the specs like <code>&lt;this&gt;</code>.</p> <p>There are two kinds of data types - specific and generic. Specific data types are related a single property or a small group of properties. For example, the <code>&lt;transform-function&gt;</code> data type is used as the value for the <code>transform</code> property alone.</p> <p>Generic data types, on the other hand, are not related to any specific property. For example we can have a value of <code>10px</code>, which is a <code>&lt;length&gt;</code> data type, for the <code>margin</code>, <code>font-size</code> or a number of other propertes.</p> <p>In this article, I'm going to give an overview of all the generic data types.</p> <table> <thead> <tr> <th>Category</th> <th>Name</th> <th>Type</th> </tr> </thead> <tbody> <tr> <td>Textual</td> <td>Keywords</td> <td><code>&lt;ident&gt;</code></td> </tr> <tr> <td>Custom Keywords</td> <td><code>&lt;custom-ident&gt;</code></td> <td></td> </tr> <tr> <td>Quoted Strings</td> <td><code>&lt;string&gt;</code></td> <td></td> </tr> <tr> <td>Resource Locators</td> <td><code>&lt;url&gt;</code></td> <td></td> </tr> <tr> <td>Basic Numeric</td> <td>Integers</td> <td><code>&lt;integer&gt;</code></td> </tr> <tr> <td>Real Numbers</td> <td><code>&lt;number&gt;</code></td> <td></td> </tr> <tr> <td>Ratios</td> <td><code>&lt;ratio&gt;</code></td> <td></td> </tr> <tr> <td>Percentages</td> <td><code>&lt;percentage&gt;</code></td> <td></td> </tr> <tr> <td>Dimensions</td> <td>Distances</td> <td><code>&lt;length&gt;</code></td> </tr> <tr> <td>Angles</td> <td><code>&lt;angle&gt;</code></td> <td></td> </tr> <tr> <td>Duration</td> <td><code>&lt;duration&gt;</code></td> <td></td> </tr> <tr> <td>Frequency</td> <td><code>&lt;frequency&gt;</code></td> <td></td> </tr> <tr> <td>Resolution</td> <td><code>&lt;resolution&gt;</code></td> <td></td> </tr> <tr> <td>Other</td> <td>Colours</td> <td><code>&lt;color&gt;</code></td> </tr> <tr> <td>Images</td> <td><code>&lt;image&gt;</code></td> <td></td> </tr> <tr> <td>Position</td> <td><code>&lt;position&gt;</code></td> <td></td> </tr> </tbody> </table> <h2 id="textual-data-types" tabindex="-1">Textual Data Types <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h2> <h3 id="keywords" tabindex="-1">Keywords <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>The <code>&lt;ident&gt;</code> data type refers to the pre-defined keywords in CSS. This includes both the unique values for certain properties, for example <code>block</code> for the <code>display</code> property, as well as the CSS-wide values <code>initial</code>, <code>inherit</code> and <code>unset</code> (See <a href="https://bitsofco.de/initial-inherit-unset-and-revert">Initial, Inherit, Unset, and Revert</a>).</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">border-color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span><br> <span class="token property">position</span><span class="token punctuation">:</span> inherit<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>These keywords are case-insensitive and must always be written without quotes, less they be misinterpreted as the <code>&lt;string&gt;</code> data type.</p> <h3 id="custom-keywords" tabindex="-1">Custom Keywords <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>The <code>&lt;custom-ident&gt;</code> (also referred to as <code>&lt;user-ident&gt;</code>) data type refers to keywords that have been defined by the author of the stylesheet. There are restrictions as to what can be a valid <code>&lt;custom-ident&gt;</code>, for example it cannot be one of the CSS-wide values.</p> <p>A common example of a custom keyword is the value for the <code>animation-name</code> property. This property can accept as its value the name of a custom animation, as defined by the stylesheet author.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@keyframes</span> hulkify</span> <span class="token punctuation">{</span>
<br> <span class="token selector">from</span> <span class="token punctuation">{</span><br> <span class="token property">color</span><span class="token punctuation">:</span> pink<span class="token punctuation">;</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>1<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span>
<br> <span class="token selector">to</span> <span class="token punctuation">{</span><br> <span class="token property">color</span><span class="token punctuation">:</span> green<span class="token punctuation">;</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>2<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span>
<br><span class="token punctuation">}</span><br><span class="token selector">.bruce-banner</span> <span class="token punctuation">{</span> <span class="token property">animation-name</span><span class="token punctuation">:</span> hulkify<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <h3 id="quoted-strings" tabindex="-1">Quoted Strings <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>The <code>&lt;string&gt;</code> data type refers to any quoted string. The text within the quotes can be any sequence of Unicode characters.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo::after</span> <span class="token punctuation">{</span><br> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"Hello, world!"</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><span class="token selector">.foo::before</span> <span class="token punctuation">{</span><br> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"We can add 'quotes' within quotes \A And move to a separate line"</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="resource-locators" tabindex="-1">Resource Locators <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h2> <p>The <code>&lt;url&gt;</code> data type is used to reference a resource file or fragment. This data type is typically written using the <code>url()</code> function, but can be represented as a <code>&lt;string&gt;</code> in some cases, notably with the <code>@import</code> rule.</p> <p>There are three types of URLs that can be used with this data type -</p> <ul> <li><strong>Absolute</strong> URLs are those that specify a protocol and domain. The resource these URLs direct to are not necessarily in the same domain as the stylesheet they are referred from.</li> <li><strong>Relative</strong> URLs are those that are refer to a file using the referring stylesheet's location as the base location.</li> <li><strong>Fragment</strong> URLs are used to refer to elements in the host document itself. The reference is the ID of the element, not a path to a file.</li> </ul> <pre class="language-css" tabindex="0"><code class="language-css"> <span class="token comment">/* Absolute URL */</span><br><span class="token atrule"><span class="token rule">@import</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">"https://fonts.googleapis.com/css?family=Source+Sans+Pro:400"</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span></span><br><br> <span class="token comment">/* Realtive URL */</span><br><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">"../img/bg.png"</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token atrule"><span class="token rule">@import</span> <span class="token string">"components/buttons.css"</span><span class="token punctuation">;</span></span><br><br> <span class="token comment">/* Fragment URL */</span><br><span class="token selector">.bar</span> <span class="token punctuation">{</span> <span class="token property">filter</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">"#blurFilter"</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <h2 id="basic-numeric-data-types" tabindex="-1">Basic Numeric Data Types <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h2> <h3 id="integers" tabindex="-1">Integers <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>An <code>&lt;integer&gt;</code> data type is the same as an integer as defined mathematically. It is a whole number, i.e. not a fraction, and can be position or negative. The first digit may be preceded by a <code>-</code> or <code>+</code> to indicate it's sign, although a <code>+</code> is implied if nothing is specified.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">z-index</span><span class="token punctuation">:</span> 10<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">z-index</span><span class="token punctuation">:</span> +10<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.bar</span> <span class="token punctuation">{</span> <span class="token property">z-index</span><span class="token punctuation">:</span> -10<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <h3 id="real-numbers" tabindex="-1">Real Numbers <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>The <code>&lt;number&gt;</code> data type is a &quot;real number&quot;. It can be an <code>&lt;integer&gt;</code>, <code>0</code>, or a fraction written as a decimal. Like integers, real numbers can be positive or negative, as indicated by a sign preceding the first digit.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">line-height</span><span class="token punctuation">:</span> 3<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.bar</span> <span class="token punctuation">{</span> <span class="token property">line-height</span><span class="token punctuation">:</span> -2.5<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">line-height</span><span class="token punctuation">:</span> +5.5<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <h3 id="ratios" tabindex="-1">Ratios <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>The <code>&lt;ratio&gt;</code> data type specifies a relationship between two numbers, specifically two positive <code>&lt;integer&gt;</code>s. Although ratios can be written in different ways in mathematics, in CSS they are always written as <code>&lt;integer&gt; / &lt;integer&gt;</code>.</p> <p>Ratio data types are typically used in media queries to target device aspect ratios.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">device-aspect-ratio</span><span class="token punctuation">:</span> 16/9<span class="token punctuation">)</span></span> <span class="token punctuation">{</span> <span class="token comment">/* Wide screen displays, iPhone 5 */</span> <span class="token punctuation">}</span><br><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">device-aspect-ratio</span><span class="token punctuation">:</span> 4/3<span class="token punctuation">)</span></span> <span class="token punctuation">{</span> … <span class="token punctuation">}</span></code></pre> <h3 id="percentages" tabindex="-1">Percentages <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>The <code>&lt;percentage&gt;</code> data type is made up of a <code>&lt;number&gt;</code> followed by a <code>%</code> sign. It represents a value that is a fraction of some other value with it's own data type. Therefore, we can have different kinds of the percentage data type, depending on what data type the value is.</p> <ul> <li>The <code>&lt;length-percentage&gt;</code> data type represents a fraction of a <code>&lt;length&gt;</code> value.</li> <li>The <code>&lt;number-percentage&gt;</code> data type represents a fraction of a <code>&lt;number&gt;</code> value.</li> <li>The <code>&lt;angle-percentage&gt;</code> data type represents a fraction of a <code>&lt;angle&gt;</code> value.</li> <li>The <code>&lt;time-percentage&gt;</code> data type represents a fraction of a <code>&lt;time&gt;</code> value.</li> <li>The <code>&lt;frequency-percentage&gt;</code> data type represents a fraction of a <code>&lt;frequency&gt;</code> value.</li> </ul> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span> <span class="token comment">/* &lt;length-percentage> */</span><br> <span class="token property">line-height</span><span class="token punctuation">:</span> 200% <span class="token comment">/* &lt;number-percentage> */</span><br> <span class="token property">voice-pitch</span><span class="token punctuation">:</span> 25% <span class="token comment">/* &lt;frequency-percentage> */</span><br><span class="token punctuation">}</span></code></pre> <h2 id="dimensional-data-types" tabindex="-1">Dimensional Data Types <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h2> <p>Dimensions are numeric data types, specifically <code>&lt;number&gt;</code>s, but qualified by a unit of measurement. They are written as a <code>&lt;number&gt;</code> followed by the unit identifier. When the <code>&lt;number&gt;</code> is <code>0</code>, however, it can be written without it's unit.</p> <h3 id="distances" tabindex="-1">Distances <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>The <code>&lt;length&gt;</code> data type represents units of distance. There are two types of length units.</p> <ul> <li><strong>Absolute units</strong>, for example <code>px</code>, <code>cm</code>, and <code>pt</code>, are fixed and (mostly) relate to some physical measurement. Once they are declared, their size cannot be altered by changing the font size of a containing element.</li> <li><strong>Relative units</strong>, for example <code>em</code>, <code>rem</code>, and the viewport units, do not have an objective measurement. Instead, their actual size is determined by the size of a parent element. This means that their size can be altered by changing the sizing of that dependent element.</li> </ul> <p>(See <a href="https://bitsofco.de/css-font-sizing">CSS Font Sizing</a>)</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 16px<span class="token punctuation">;</span> <span class="token comment">/* absolute */</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 50vw<span class="token punctuation">;</span> <span class="token comment">/* relative */</span><br><span class="token punctuation">}</span></code></pre> <h3 id="angles" tabindex="-1">Angles <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>The <code>&lt;angle&gt;</code> data type represents an angle of a circle. There are four units we can use to define this angular dimension.</p> <ul> <li>The<code>deg</code> unit represents the angle in <strong>Degrees</strong>. There are 360 degrees in a full circle.</li> <li>The<code>grad</code> unit represents the angle in <strong>Gradians</strong>. There are 400 gradians in a full circle.</li> <li>The<code>rad</code> unit represents the angle in <strong>Radians</strong>. There are 2πrad in a full circle (roughly 57.29rad).</li> <li>The<code>turn</code> unit represents the angle in <strong>Turns</strong>. There is 1 turn in a full circle.</li> </ul> <p>These units can be positive or negative, denoting if the turn is happening clockwise or anti-clockwise, respectively. Below is an example of how a 90 degrees clockwise turn can be written in each of these units.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token comment">/* Going clockwise */</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span>90deg<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span>100grad<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span>0.25turn<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span>1.57rad<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">/* Going anti-clockwise */</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span>-270deg<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span>-300grad<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span>-1.25turn<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span>-55.72rad<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h3 id="duration" tabindex="-1">Duration <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>The <code>&lt;time&gt;</code> data type represents a unit of time. There are two units we can use to define duration.</p> <ul> <li>The <code>s</code> unit represents a <strong>Second</strong>.</li> <li>The <code>ms</code> units represents a <strong>Millisecond</strong>. There are 1000 milliseconds in one second.</li> </ul> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">transition-duration</span><span class="token punctuation">:</span> 1s<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.bar</span> <span class="token punctuation">{</span> <span class="token property">transition-duration</span><span class="token punctuation">:</span> 1000ms<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <h3 id="frequency" tabindex="-1">Frequency <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>The <code>&lt;frequency&gt;</code> data type represents the sound frequency. There are two units we can use to define frequency.</p> <ul> <li>The <code>kHz</code> unit represents a <strong>KiloHertz</strong>.</li> <li>The <code>Hz</code> units represents a <strong>Hertz</strong>. There are 1000 KiloHertz in one Hertz.</li> </ul> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">voice-pitch</span><span class="token punctuation">:</span> 250Hz<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.bar</span> <span class="token punctuation">{</span> <span class="token property">voice-pitch</span><span class="token punctuation">:</span> 1kHz<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <h3 id="resolution" tabindex="-1">Resolution <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>The <code>&lt;resolution&gt;</code> data type represents resolution of the user's current device. The resolution itself is the size of a single &quot;dot&quot;. The size of this dot is calculated by indicating how many of them will fit in a CSS inch, centemetre, or pixel. Depending on which CSS unit we are using, we can specify resolution in one of four units.</p> <ul> <li>The <code>dpi</code> unit represents a represents how many dots will fit in a CSS <strong>Inch</strong>.</li> <li>The <code>dpcm</code> unit represents a represents how many dots will fit in a CSS <strong>Centemetre</strong>.</li> <li>The <code>dppx</code> unit represents a represents how many dots will fit in a CSS <strong>Pixel</strong>.</li> </ul> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">min-resolution</span><span class="token punctuation">:</span> 100ddpx<span class="token punctuation">)</span></span> <span class="token punctuation">{</span> .. <span class="token punctuation">}</span><br><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">min-resolution</span><span class="token punctuation">:</span> 100dpcm<span class="token punctuation">)</span></span> <span class="token punctuation">{</span> .. <span class="token punctuation">}</span><br><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">min-resolution</span><span class="token punctuation">:</span> 300dpi<span class="token punctuation">)</span></span> <span class="token punctuation">{</span> <span class="token comment">/* Retina display */</span> <span class="token punctuation">}</span></code></pre> <h2 id="other-data-types" tabindex="-1">Other Data Types <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h2> <h3 id="colours" tabindex="-1">Colours <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>The <code>&lt;color&gt;</code> data type is used to define a colour value. The data type can be one of two formats.</p> <ul> <li>A <strong>keyword</strong>, which can either one of the pre-defined colours (e.g. <code>cornflowerblue</code>), the <code>transparent</code> keyword, or the <code>currentColor</code> keyword.</li> <li>A <strong>numeric</strong> value using one of the colour notations; <code>#rgb``rgb()</code>, <code>rgba()</code>, <code>hsl()</code>, <code>hsla()</code>.</li> </ul> <p>Below is an example of how we can achieve the colour black using these different formats.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">color</span><span class="token punctuation">:</span> black<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 property">color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span>0<span class="token punctuation">,</span>0<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">color</span><span class="token punctuation">:</span> <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>1<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span>0%<span class="token punctuation">,</span>0%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">hsla</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span>0%<span class="token punctuation">,</span>0%<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h3 id="images" tabindex="-1">Images <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>The <code>&lt;image&gt;</code> data type represents a 2D image. It can be one of three formats.</p> <ul> <li>A <strong>URL reference</strong>, specified using the <code>&lt;url&gt;</code> data type.</li> <li>An <strong>element</strong> in the document, using the <code>element()</code> function. (Note - This function is has very limited support).</li> <li>A <strong>gradient function</strong>, using the <code>&lt;gradient&gt;</code> data type.</li> </ul> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">'path/to/bg.png'</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.bar</span> <span class="token punctuation">{</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">element</span><span class="token punctuation">(</span><span class="token string">'#background'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.baz</span> <span class="token punctuation">{</span> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span>white<span class="token punctuation">,</span> gray<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <h3 id="position" tabindex="-1">Position <a class="header-anchor" href="https://bitsofco.de/generic-css-data-types/">#</a></h3> <p>The <code>&lt;position&gt;</code> data type denotes the position of an element within a containing area or element. It can be one of three other data types -</p> <ul> <li>A <strong>keyword</strong> of either <code>top</code>, <code>right</code>, <code>bottom</code>, <code>left</code>, or <code>center</code>.</li> <li>A <strong>length</strong> value.</li> <li>A <strong>percentage</strong>, specifically <code>&lt;length-percentage&gt;</code>, value.</li> </ul> <p>Below is an example of how to achieve getting a background image (100x100px) to be positioned in the bottom right hand corner of it's container (300x300px) -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">background-position</span><span class="token punctuation">:</span> right bottom<span class="token punctuation">;</span><br> <span class="token property">background-position</span><span class="token punctuation">:</span> 200px 200px<span class="token punctuation">;</span><br> <span class="token property">background-position</span><span class="token punctuation">:</span> 100% 100%<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> Realtime Form Validation 2016-06-21T00:00:00Z https://bitsofco.de/realtime-form-validation/ <p>Last week, I went over some <a href="https://bitsofco.de/form-validation-techniques">Techniques for Form Validation</a>, including using CSS for visual feedback as well as the Constraint Validation API. I also discussed the benefits of validating inputs in realtime, and so this week I built out this simple registration form, demonstrating realtime validation.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/LEbyWqGekO-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/LEbyWqGekO-720.gif 720w"><img alt="Realtime Form Validation Demo" loading="lazy" decoding="async" src="https://bitsofco.de/img/LEbyWqGekO-720.webp" width="720" height="401"></picture></p> <p><a href="https://ireade.github.io/form-validation-realtime/">View Live Demo</a></p> <p>There are three key features of this form -</p> <ol> <li>The requirements for each input are clearly displayed even before the user starts typing</li> <li>As the user starts typing on the field and fulfils each requirement, they are given real time feedback on their success (or failings)</li> <li>The feedback is presented in such a way that the user will not submit the form without knowing of the errors in their input</li> </ol> <p>If you'd like to see a walkthrough of how I built this out, you can watch the video below.</p> <p><a href="https://www.youtube.com/watch?v=m4Fru330HqQ"><picture><source type="image/avif" srcset="https://bitsofco.de/img/O8tWrOTvRx-1280.avif 1280w"><source type="image/webp" srcset="https://bitsofco.de/img/O8tWrOTvRx-1280.webp 1280w"><img alt="Realtime Form Validation" loading="lazy" decoding="async" src="https://bitsofco.de/img/O8tWrOTvRx-1280.png" width="1280" height="720"></picture></a></p> <p>And here is the JavaScript behind the realtime validation -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* ----------------------------<br><br> CustomValidation prototype<br><br> - Keeps track of the list of invalidity messages for this input<br> - Keeps track of what validity checks need to be performed for this input<br> - Performs the validity checks and sends feedback to the front end<br><br>---------------------------- */</span><br><br><span class="token keyword">function</span> <span class="token function">CustomValidation</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span>invalidities <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">this</span><span class="token punctuation">.</span>validityChecks <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><br><br><span class="token class-name">CustomValidation</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token function-variable function">addInvalidity</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">message</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span>invalidities<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>message<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 function-variable function">getInvalidities</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>invalidities<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">'. \n'</span><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 function-variable function">checkValidity</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">input</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>validityChecks<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 keyword">var</span> isInvalid <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>validityChecks<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">isInvalid</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>isInvalid<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">addInvalidity</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>validityChecks<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>invalidityMessage<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token keyword">var</span> requirementElement <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>validityChecks<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>element<span class="token punctuation">;</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>requirementElement<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>isInvalid<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> requirementElement<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">'invalid'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> requirementElement<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">'valid'</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> requirementElement<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">'invalid'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> requirementElement<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">'valid'</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 comment">// end if requirementElement</span><br> <span class="token punctuation">}</span> <span class="token comment">// end for</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span><br><br><br><br><span class="token comment">/* ----------------------------<br><br> Validity Checks<br><br> The arrays of validity checks for each input<br> Comprised of three things<br> 1. isInvalid() - the function to determine if the input fulfills a particular requirement<br> 2. invalidityMessage - the error message to display if the field is invalid<br> 3. element - The element that states the requirement<br><br>---------------------------- */</span><br><br><span class="token keyword">var</span> usernameValidityChecks <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token function-variable function">isInvalid</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">input</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> input<span class="token punctuation">.</span>value<span class="token punctuation">.</span>length <span class="token operator">&lt;</span> <span class="token number">3</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">invalidityMessage</span><span class="token operator">:</span> <span class="token string">'This input needs to be at least 3 characters'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">element</span><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">'label[for="username"] .input-requirements li:nth-child(1)'</span><span class="token punctuation">)</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token function-variable function">isInvalid</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">input</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">var</span> illegalCharacters <span class="token operator">=</span> input<span class="token punctuation">.</span>value<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[^a-zA-Z0-9]</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> illegalCharacters <span class="token operator">?</span> <span class="token boolean">true</span> <span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">invalidityMessage</span><span class="token operator">:</span> <span class="token string">'Only letters and numbers are allowed'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">element</span><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">'label[for="username"] .input-requirements li:nth-child(2)'</span><span class="token punctuation">)</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">]</span><span class="token punctuation">;</span><br><br><span class="token keyword">var</span> passwordValidityChecks <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token function-variable function">isInvalid</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">input</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> input<span class="token punctuation">.</span>value<span class="token punctuation">.</span>length <span class="token operator">&lt;</span> <span class="token number">8</span> <span class="token operator">|</span> input<span class="token punctuation">.</span>value<span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">100</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">invalidityMessage</span><span class="token operator">:</span> <span class="token string">'This input needs to be between 8 and 100 characters'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">element</span><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">'label[for="password"] .input-requirements li:nth-child(1)'</span><span class="token punctuation">)</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token function-variable function">isInvalid</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">input</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> <span class="token operator">!</span>input<span class="token punctuation">.</span>value<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[0-9]</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><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 literal-property property">invalidityMessage</span><span class="token operator">:</span> <span class="token string">'At least 1 number is required'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">element</span><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">'label[for="password"] .input-requirements li:nth-child(2)'</span><span class="token punctuation">)</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token function-variable function">isInvalid</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">input</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> <span class="token operator">!</span>input<span class="token punctuation">.</span>value<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[a-z]</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><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 literal-property property">invalidityMessage</span><span class="token operator">:</span> <span class="token string">'At least 1 lowercase letter is required'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">element</span><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">'label[for="password"] .input-requirements li:nth-child(3)'</span><span class="token punctuation">)</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token function-variable function">isInvalid</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">input</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> <span class="token operator">!</span>input<span class="token punctuation">.</span>value<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[A-Z]</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><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 literal-property property">invalidityMessage</span><span class="token operator">:</span> <span class="token string">'At least 1 uppercase letter is required'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">element</span><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">'label[for="password"] .input-requirements li:nth-child(4)'</span><span class="token punctuation">)</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token function-variable function">isInvalid</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">input</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> <span class="token operator">!</span>input<span class="token punctuation">.</span>value<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[\!\@\#\$\%\^\&amp;\*]</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><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 literal-property property">invalidityMessage</span><span class="token operator">:</span> <span class="token string">'You need one of the required special characters'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">element</span><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">'label[for="password"] .input-requirements li:nth-child(5)'</span><span class="token punctuation">)</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">]</span><span class="token punctuation">;</span><br><br><span class="token keyword">var</span> passwordRepeatValidityChecks <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token function-variable function">isInvalid</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> passwordRepeatInput<span class="token punctuation">.</span>value <span class="token operator">!=</span> passwordInput<span class="token punctuation">.</span>value<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token literal-property property">invalidityMessage</span><span class="token operator">:</span> <span class="token string">'This password needs to match the first one'</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">]</span><span class="token punctuation">;</span><br><br><br><br><span class="token comment">/* ----------------------------<br><br> Check this input<br><br> Function to check this particular input<br> If input is invalid, use setCustomValidity() to pass a message to be displayed<br><br>---------------------------- */</span><br><br><span class="token keyword">function</span> <span class="token function">checkInput</span><span class="token punctuation">(</span><span class="token parameter">input</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> input<span class="token punctuation">.</span>CustomValidation<span class="token punctuation">.</span>invalidities <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br> input<span class="token punctuation">.</span>CustomValidation<span class="token punctuation">.</span><span class="token function">checkValidity</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> input<span class="token punctuation">.</span>CustomValidation<span class="token punctuation">.</span>invalidities<span class="token punctuation">.</span>length <span class="token operator">==</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</span> input<span class="token punctuation">.</span>value <span class="token operator">!=</span> <span class="token string">''</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> input<span class="token punctuation">.</span><span class="token function">setCustomValidity</span><span class="token punctuation">(</span><span class="token string">''</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> <span class="token keyword">var</span> message <span class="token operator">=</span> input<span class="token punctuation">.</span>CustomValidation<span class="token punctuation">.</span><span class="token function">getInvalidities</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> input<span class="token punctuation">.</span><span class="token function">setCustomValidity</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><br><br><br><br><span class="token comment">/* ----------------------------<br><br> Setup CustomValidation<br><br> Setup the CustomValidation prototype for each input<br> Also sets which array of validity checks to use for that input<br><br>---------------------------- */</span><br><br><span class="token keyword">var</span> usernameInput <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'username'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">var</span> passwordInput <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'password'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">var</span> passwordRepeatInput <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'password_repeat'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>usernameInput<span class="token punctuation">.</span>CustomValidation <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CustomValidation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>usernameInput<span class="token punctuation">.</span>CustomValidation<span class="token punctuation">.</span>validityChecks <span class="token operator">=</span> usernameValidityChecks<span class="token punctuation">;</span><br><br>passwordInput<span class="token punctuation">.</span>CustomValidation <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CustomValidation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>passwordInput<span class="token punctuation">.</span>CustomValidation<span class="token punctuation">.</span>validityChecks <span class="token operator">=</span> passwordValidityChecks<span class="token punctuation">;</span><br><br>passwordRepeatInput<span class="token punctuation">.</span>CustomValidation <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CustomValidation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br>passwordRepeatInput<span class="token punctuation">.</span>CustomValidation<span class="token punctuation">.</span>validityChecks <span class="token operator">=</span> passwordRepeatValidityChecks<span class="token punctuation">;</span><br><br><br><br><br><span class="token comment">/* ----------------------------<br><br> Event Listeners<br><br>---------------------------- */</span><br><br><span class="token keyword">var</span> inputs <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'input:not([type="submit"])'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">var</span> submit <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">'input[type="submit"'</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">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> inputs<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> inputs<span class="token punctuation">[</span>i<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">'keyup'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">checkInput</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 punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br>submit<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 punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> inputs<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 function">checkInput</span><span class="token punctuation">(</span>inputs<span class="token punctuation">[</span>i<span class="token punctuation">]</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><br></code></pre> <p><a href="https://github.com/ireade/form-validation-realtime">Full Source Code</a> | <a href="https://ireade.github.io/form-validation-realtime/">Live Demo</a></p> <p>As always, there are improvements that can be made and since I built and recorded this I've already had some ideas for how to improve the form. For example -</p> <ul> <li>Give feedback when a user focuses out of an invalid input</li> <li>Use a custom tooltip in place of the in-built browser tooltips for more consistent and reliable behaviour across browsers</li> </ul> <p>If you have more ideas, or feedback on the implementation, do leave a comment below.</p> Form Validation Techniques 2016-06-14T00:00:00Z https://bitsofco.de/form-validation-techniques/ <p>We all know that online forms can be a hassle to have to fill out. Especially when there is nothing but a list of inputs presented without much guidance or feedback. But as form creators, there are many things we can do to improve the experience for the users filling out forms online.</p> <h2 id="using-css" tabindex="-1">Using CSS <a class="header-anchor" href="https://bitsofco.de/form-validation-techniques/">#</a></h2> <p>Using CSS, we have access to four states of a form input with pseudo-classes - <code>:valid</code>, <code>:invalid</code>, <code>:required</code> and <code>:optional</code>. We can use these states to provide some - albeit limited - feedback to users as they fill out the form.</p> <p>Using <code>:valid</code> and <code>:invalid</code>, we can let users know, in real time, if their input satisfies all the requirements necessary.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">input:valid</span> <span class="token punctuation">{</span> <span class="token property">border-color</span><span class="token punctuation">:</span> green<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">input:invalid</span> <span class="token punctuation">{</span> <span class="token property">border-color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/KcxO03x8Uj-815.avif 815w"><source type="image/webp" srcset="https://bitsofco.de/img/KcxO03x8Uj-815.webp 815w"><img alt="Styling valid and invalid pseudo-classes" loading="lazy" decoding="async" src="https://bitsofco.de/img/KcxO03x8Uj-815.png" width="815" height="298"></picture></p> <p>One problem with using this method, however, is that the styles are applied even before the user starts to act on the form. For required inputs, they will show us as <code>:invalid</code>, and optional inputs will show as <code>:valid</code>. This means that before the user is even given the chance to start, they may already be given negative feedback, which isn’t a great experience.</p> <p>Styling <code>:required</code> and <code>:optional</code> states alone is typically not very useful either as this is information that is usually given in a label. However, we may want to combine these states with the <code>:valid</code>/<code>:invalid</code> pseudo-classes and only style certain combinations. For example, we may want to only give positive feedback when a required input is valid.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">input:required:valid</span> <span class="token punctuation">{</span> <span class="token property">border-color</span><span class="token punctuation">:</span> green<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/TAUsmvs30F-806.avif 806w"><source type="image/gif" srcset="https://bitsofco.de/img/TAUsmvs30F-806.gif 806w"><img alt="Styling on valid required inputs" loading="lazy" decoding="async" src="https://bitsofco.de/img/TAUsmvs30F-806.webp" width="806" height="290"></picture></p> <h2 id="using-javascript" tabindex="-1">Using JavaScript <a class="header-anchor" href="https://bitsofco.de/form-validation-techniques/">#</a></h2> <p>Using JavaScript, we can create a much better user experience when filling out forms. For example, consider these three number inputs, each of them requiring a minimum value of 10, a maximum of 100, and a step of 10.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span><br> Number Input 1<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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 attr-name">min</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">max</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name">step</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 punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span><br> Number Input 2<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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 attr-name">min</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">max</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name">step</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 punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span><br> Number Input 3<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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 attr-name">min</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">max</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>100<span class="token punctuation">"</span></span> <span class="token attr-name">step</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 punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>submit<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">&lt;/</span>form</span><span class="token punctuation">></span></span></code></pre> <p>By setting the <code>min</code>, <code>max</code> and <code>step</code> attributes, we can control the number the user inputs <em>only if</em> they use the controls on the input element. However, this doesn’t stop the user from manually typing in invalid numbers. If they type in <code>1</code>, <code>12</code>, and <code>123</code> respectively to the three inputs and submits the form, this is what happens -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/SP8LAxjPf_-814.avif 814w"><source type="image/webp" srcset="https://bitsofco.de/img/SP8LAxjPf_-814.webp 814w"><img alt="Default validation message tooltip" loading="lazy" decoding="async" src="https://bitsofco.de/img/SP8LAxjPf_-814.png" width="814" height="387"></picture></p> <p>What happens is the user only gets an error message for the first input and they only get one error message, even though the input violates 2 of the requirements. We can fix this by modifying the validity message being passed.</p> <h3 id="adding-multiple-error-messages-to-one-tooltip" tabindex="-1">Adding multiple error messages to one tooltip <a class="header-anchor" href="https://bitsofco.de/form-validation-techniques/">#</a></h3> <p>When validating inputs, the browser does checks for a defined list of potential invalidites. There is an object, <code>validity</code>, that is attached to all inputs. It contains a list of keys with boolean values, each checking if a certain type of invalidity applies to this input. For example, this is the validity object returned when the user inputs <code>1</code> on the previous input.</p> <pre class="language-js" tabindex="0"><code class="language-js">input<span class="token punctuation">.</span>validity <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">valid</span><span class="token operator">:</span><span class="token boolean">false</span> <span class="token comment">// If the input is valid</span><br> <span class="token literal-property property">customError</span><span class="token operator">:</span><span class="token boolean">false</span> <span class="token comment">// If a custom error message has been set</span><br> <span class="token literal-property property">patternMismatch</span><span class="token operator">:</span><span class="token boolean">false</span> <span class="token comment">// If the invalidity is against the pattern attribute</span><br> <span class="token literal-property property">rangeOverflow</span><span class="token operator">:</span><span class="token boolean">false</span> <span class="token comment">// If the invalidity is against the max attribute</span><br> <span class="token literal-property property">rangeUnderflow</span><span class="token operator">:</span><span class="token boolean">true</span> <span class="token comment">// If the invalidity is against the min attribute</span><br> <span class="token literal-property property">stepMismatch</span><span class="token operator">:</span><span class="token boolean">true</span> <span class="token comment">// If the invalidity is against the step attribute</span><br> <span class="token literal-property property">tooLong</span><span class="token operator">:</span><span class="token boolean">false</span> <span class="token comment">// If the invalidity is against the maxlength attribute</span><br> <span class="token literal-property property">tooShort</span><span class="token operator">:</span><span class="token boolean">false</span> <span class="token comment">// If the invalidity is against the minlength attribute</span><br> <span class="token literal-property property">typeMismatch</span><span class="token operator">:</span><span class="token boolean">false</span> <span class="token comment">// If the invalidity is against the type attribute</span><br> <span class="token literal-property property">valueMissing</span><span class="token operator">:</span><span class="token boolean">false</span> <span class="token comment">// If the input is required but empty</span><br><span class="token punctuation">}</span></code></pre> <p>By default, the browser will only display one of these invalidities. So what we can do instead is check each of these values ourselves and, if we find an invalidity, store it. Once we have stored all the invalidity messages for an input, we can then set the whole list as a custom validity message using the <code>setCustomValidity()</code> function.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">CustomValidation</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 class-name">CustomValidation</span><span class="token punctuation">.</span>prototype <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token comment">// Set default empty array of invalidity messages</span><br> <span class="token literal-property property">invalidities</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">// Function to check validity</span><br> <span class="token function-variable function">checkValidity</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">input</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">var</span> validity <span class="token operator">=</span> input<span class="token punctuation">.</span>validity<span class="token punctuation">;</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> validity<span class="token punctuation">.</span>patternMismatch <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">addInvalidity</span><span class="token punctuation">(</span><span class="token string">'This is the wrong pattern for this field'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> validity<span class="token punctuation">.</span>rangeOverflow <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">var</span> max <span class="token operator">=</span> <span class="token function">getAttributeValue</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> <span class="token string">'max'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">addInvalidity</span><span class="token punctuation">(</span><span class="token string">'The maximum value should be '</span> <span class="token operator">+</span> max<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> validity<span class="token punctuation">.</span>rangeUnderflow <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">var</span> min <span class="token operator">=</span> <span class="token function">getAttributeValue</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> <span class="token string">'min'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">addInvalidity</span><span class="token punctuation">(</span><span class="token string">'The minimum value should be '</span> <span class="token operator">+</span> min<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> validity<span class="token punctuation">.</span>stepMismatch <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">var</span> step <span class="token operator">=</span> <span class="token function">getAttributeValue</span><span class="token punctuation">(</span>input<span class="token punctuation">,</span> <span class="token string">'step'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">addInvalidity</span><span class="token punctuation">(</span><span class="token string">'This number needs to be a multiple of '</span> <span class="token operator">+</span> step<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token comment">// Additional validity checks here...</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br><br> <span class="token comment">// Add invalidity message to invalidities object</span><br> <span class="token function-variable function">addInvalidity</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">message</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span>invalidities<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>message<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 comment">// Retrieve the invalidity messages</span><br> <span class="token function-variable function">getInvalidities</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>invalidities<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">'. \n'</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><br><br><span class="token comment">// On click of form submit buttons</span><br>submit<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">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Loop through all inputs</span><br> <span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> inputs<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 keyword">var</span> input <span class="token operator">=</span> inputs<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Use native JavaScript checkValidity() function to check if input is valid</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> input<span class="token punctuation">.</span><span class="token function">checkValidity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token boolean">false</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">var</span> inputCustomValidation <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CustomValidation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// New instance of CustomValidation</span><br> inputCustomValidation<span class="token punctuation">.</span><span class="token function">checkValidity</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Check Invalidities</span><br> <span class="token keyword">var</span> customValidityMessage <span class="token operator">=</span> inputCustomValidation<span class="token punctuation">.</span><span class="token function">getInvalidities</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Get custom invalidity messages</span><br> input<span class="token punctuation">.</span><span class="token function">setCustomValidity</span><span class="token punctuation">(</span> customValidityMessage <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// set as custom validity message</span><br><br> <span class="token punctuation">}</span> <span class="token comment">// end if</span><br> <span class="token punctuation">}</span> <span class="token comment">// end loop</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br></code></pre> <p>Doing, this, when the form from above is submitted again, this is what happens -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Tg8vufAcS9-814.avif 814w"><source type="image/webp" srcset="https://bitsofco.de/img/Tg8vufAcS9-814.webp 814w"><img alt="Displaying multiple error messages to one tooltip" loading="lazy" decoding="async" src="https://bitsofco.de/img/Tg8vufAcS9-814.png" width="814" height="407"></picture></p> <p>This is better, because all the error messages for that input are now being displayed. However, we still have the problem that only the error message for the first input is being displayed.</p> <p>This is a limitation of the native browser validation message. So, we need to find an alternative.</p> <h3 id="displaying-all-error-messages-for-all-inputs" tabindex="-1">Displaying all error messages for all inputs <a class="header-anchor" href="https://bitsofco.de/form-validation-techniques/">#</a></h3> <p>Instead of using the built-in tooltip, we can add the error messages to the DOM directly. This way, all the error messages for each input will be clearly displayed next to them.</p> <p>We can do this very simply with a couple of additional lines -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token class-name">CustomValidation</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">getInvaliditiesForHTML</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>invalidities<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">'. &lt;br>'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token comment">// On click of form submit buttons</span><br>submit<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">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Loop through all inputs</span><br> <span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> inputs<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 keyword">var</span> input <span class="token operator">=</span> inputs<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Use native JavaScript checkValidity() function to check if input is valid</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> input<span class="token punctuation">.</span><span class="token function">checkValidity</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token boolean">false</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">var</span> inputCustomValidation <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CustomValidation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// New instance of CustomValidation</span><br> inputCustomValidation<span class="token punctuation">.</span><span class="token function">checkValidity</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Check Invalidities</span><br> <span class="token keyword">var</span> customValidityMessage <span class="token operator">=</span> inputCustomValidation<span class="token punctuation">.</span><span class="token function">getInvalidities</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Get custom invalidity messages</span><br> input<span class="token punctuation">.</span><span class="token function">setCustomValidity</span><span class="token punctuation">(</span> customValidityMessage <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// set as custom validity message</span><br><br> <span class="token comment">// DISPLAY ERROR MESSAGES IN DOCUMENT</span><br> <span class="token keyword">var</span> customValidityMessageForHTML <span class="token operator">=</span> inputCustomValidation<span class="token punctuation">.</span><span class="token function">getInvaliditiesForHTML</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> input<span class="token punctuation">.</span><span class="token function">insertAdjacentHTML</span><span class="token punctuation">(</span><span class="token string">'afterend'</span><span class="token punctuation">,</span> <span class="token string">'&lt;p class="error-message">'</span> <span class="token operator">+</span> customValidityMessageForHTML <span class="token operator">+</span> <span class="token string">'&lt;/p>'</span><span class="token punctuation">)</span><br> stopSubmit <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span><br><br> <span class="token punctuation">}</span> <span class="token comment">// end if</span><br> <span class="token punctuation">}</span> <span class="token comment">// end loop</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> stopSubmit <span class="token punctuation">)</span> <span class="token punctuation">{</span> e<span class="token punctuation">.</span><span class="token function">preventDefault</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></code></pre> <p>Now, this is what happens when the user clicks submit -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/gkgdmNrmmm-812.avif 812w"><source type="image/webp" srcset="https://bitsofco.de/img/gkgdmNrmmm-812.webp 812w"><img alt="Displaying all error messages for all inputs in the DOM" loading="lazy" decoding="async" src="https://bitsofco.de/img/gkgdmNrmmm-812.png" width="812" height="546"></picture></p> <h3 id="using-custom-validation-checks" tabindex="-1">Using custom validation checks <a class="header-anchor" href="https://bitsofco.de/form-validation-techniques/">#</a></h3> <p>Sometimes, the validation checks available in the browser may not be enough. We may want to check the inputted value against some custom requirements, for example if a text input requires special characters.</p> <p>Since we are already checking for each validity message separately in our <code>CustomValidation.prototype.checkValidity</code> function, we can easily add additional checks here as well.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token class-name">CustomValidation</span><span class="token punctuation">.</span>prototype<span class="token punctuation">.</span><span class="token function-variable function">checkValidity</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">input</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// In-built validity checks here ------</span><br><br> <span class="token comment">// Custom Validity checks ------</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span>input<span class="token punctuation">.</span>value<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[a-z]</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">addInvalidity</span><span class="token punctuation">(</span><span class="token string">'At least 1 lowercase letter is required'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span>input<span class="token punctuation">.</span>value<span class="token punctuation">.</span><span class="token function">match</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">[A-Z]</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">addInvalidity</span><span class="token punctuation">(</span><span class="token string">'At least 1 uppercase letter is required'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <h3 id="realtime-validation" tabindex="-1">Realtime validation <a class="header-anchor" href="https://bitsofco.de/form-validation-techniques/">#</a></h3> <p>Although this is a lot better, it is not without its flaws, the biggest being that the user has to submit the form before they get any specific feedback on what exactly is wrong with their inputs. However, the best user experience for forms is when validation happens in real time. There are three parts to this -</p> <ol> <li>The requirements for each input are clearly displayed even before the user starts typing</li> <li>As the user starts typing on the field and fulfils each requirement, they are given real time feedback on their success (or failings)</li> <li>The feedback is presented in such a way that the user will not submit the form without knowing of the errors in their input</li> </ol> <p>In next week's article (<a href="https://bitsofco.de/realtime-form-validation">see here</a>), I'm going to show how I implement real-time validation, by recreating this simple registration form -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/LEbyWqGekO-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/LEbyWqGekO-720.gif 720w"><img alt="Real-time Validation Demo" loading="lazy" decoding="async" src="https://bitsofco.de/img/LEbyWqGekO-720.webp" width="720" height="401"></picture></p> <p>If you'd like to try recreating this (or a better version) yourself before then, you can use this <a href="https://codepen.io/ire/pen/ezZjgv">boilerplate code</a>.</p> Anchors vs Buttons 2016-06-05T00:00:00Z https://bitsofco.de/anchors-vs-buttons/ <p>For the longest time, web developers (myself included) have been using <code>&lt;a&gt;</code>s, <code>&lt;div&gt;</code>s, <code>&lt;span&gt;</code>s, and everything but <code>&lt;button&gt;</code>s to create interactive clickable elements. It would typically go something 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">&lt;</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 attr-name">role</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 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">openMenuModal</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>Navigation<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span></code></pre> <div class="warning code-addendum">Don't do this</div> <p>A non-actionable or meaningless <code>href</code> is applied to the anchor element so that the user isn't navigated away from the page, and JavaScript is used to hijack the click event for the link. But there's actually an HTML element for this behaviour - the <code>&lt;button&gt;</code>, more specifically the <code>&lt;button type=“button”&gt;</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">&lt;</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 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">openMenuModal</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>Navigation<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></code></pre> <p>Despite this, the anchor element is still used to initiate interactions on a page and in many or most cases, this is simply incorrect. However, the button element is not perfect itself and there are some cases in which I think using the anchor tag is still appropriate. To understand how and why, let's first get into the difference between anchors and buttons.</p> <h2 id="what-s-the-difference" tabindex="-1">What’s the Difference? <a class="header-anchor" href="https://bitsofco.de/anchors-vs-buttons/">#</a></h2> <p>Visually, the <code>&lt;a&gt;</code> and <code>&lt;button type=“button”&gt;</code> elements can look identical if styled correctly. Semantically, on the other hand, they come with different repercussions, particularly for users who aren't interacting with the element through the simple point-and-click.</p> <h3 id="the-anchor-element" tabindex="-1">The Anchor Element <a class="header-anchor" href="https://bitsofco.de/anchors-vs-buttons/">#</a></h3> <p>The <code>&lt;a&gt;</code> element represents a hyperlink to a destination page or a section within a page. The element is labelled by its contents, which can be anything from an image to additional (non-interactive) elements like <code>&lt;div&gt;</code>s, <code>&lt;p&gt;</code>s, etc.</p> <p>Because the <code>&lt;a&gt;</code> element is related to a link, the css pseudo-classes <code>:link</code> and <code>:visited</code> are available to it. This is in addition to the user-action pseudo-classes <code>:hover</code>, <code>:active</code> and <code>:focus</code>.</p> <p>When navigating with a keyboard, the anchor element can be “clicked” by pressing the <code>enter</code> key. To screen readers, when an anchor tag is clicked, it is expected that the user be navigated away to another page or section.</p> <h3 id="the-button-element" tabindex="-1">The Button Element <a class="header-anchor" href="https://bitsofco.de/anchors-vs-buttons/">#</a></h3> <p>The <code>&lt;button&gt;</code> element, more specifically the <code>&lt;button type=“button”&gt;</code> element, does nothing. Unlike other button types, such as the <code>&lt;button type=“reset”&gt;</code> which resets the form it is nested within, the <code>&lt;button type=“button”&gt;</code> does not have any default behaviour.</p> <p>Because no link is related to the element, only the default user-action pseudo-classes (<code>:hover</code>, <code>:active</code> and <code>:focus</code>) are available to it. Nowadays, it can be styled as easily and extensively as an anchor element.</p> <p>When navigating with a keyboard, the button element can be “clicked” by pressing either the <code>enter</code> or <code>space</code> keys, the latter being more commonly used. To screen readers, when a button is clicked, it is expected that JavaScript be used to create some interaction on the page. Some screen readers will announce the element as a button, and speech recognition software should enable the button to be clicked by saying &quot;click&quot;.</p> <h2 id="what-s-the-problem" tabindex="-1">What's the Problem? <a class="header-anchor" href="https://bitsofco.de/anchors-vs-buttons/">#</a></h2> <h3 id="the-problem-with-buttons" tabindex="-1">The Problem with Buttons <a class="header-anchor" href="https://bitsofco.de/anchors-vs-buttons/">#</a></h3> <p>First, let me re-iterate the point that if your HTML 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">&lt;</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 attr-name">role</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 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">openMenuModal</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>Navigation<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span></code></pre> <div class="warning code-addendum">Don't do this</div> <p>...you should probably use a button instead.</p> <p><strong>However</strong>, in some cases, using the button element alone isn't necessarily the most suitable solution either.</p> <p>The <code>&lt;button type=“button”&gt;</code> element, as mentioned, by default does nothing. JavaScript must be used to add functionality to it. Therefore, if the user doesn’t have JavaScript enabled on their device, then they can no longer access the content behind the interaction, in this case the navigation menu.</p> <p>Although we have done right by screen reader users by using semantic markup, we have neglected users who can't/don't enable JavaScript by putting a critical user interaction behind this wall.</p> <h3 id="the-problem-with-anchors" tabindex="-1">The Problem with Anchors <a class="header-anchor" href="https://bitsofco.de/anchors-vs-buttons/">#</a></h3> <p>A common solution to the problem with the button element is to use the anchor element instead, but modify it in two ways -</p> <ol> <li>Provide a meaningful fallback link in the <code>href</code> attribute; and</li> <li>Alter the semantics of the anchor element so it looks and behaves like a button to screen readers</li> </ol> <p>For example, the anchor-button will 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">&lt;</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>#navigation-alternative<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>button<span class="token punctuation">"</span></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"><span class="token function">openMenu</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>Navigation<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span></code></pre> <p>This way, if a user does not have JavaScript enabled they can still access the contents of the modal further down the page. Additionally, because the button element is typically &quot;clicked&quot; with the <code>space</code> key, we can also bind that event to the anchor-button element.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> <span class="token function-variable function">bindSpaceKey</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">anchor</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> anchor<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 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 keyword">if</span> <span class="token punctuation">(</span>event<span class="token punctuation">.</span>keyCode <span class="token operator">==</span> <span class="token number">32</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> anchor<span class="token punctuation">.</span><span class="token function">click</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><br><span class="token punctuation">}</span><br><span class="token keyword">var</span> anchorButtons <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'a[role="button"]'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> anchorButtons<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 function">bindSpaceKey</span><span class="token punctuation">(</span> anchorButtons<span class="token punctuation">[</span>i<span class="token punctuation">]</span> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Even though, in my opinion, this solution is better than using a simple button, it isn't perfect either. First, if JavaScript is not enabled then the anchor element should semantically be a link, not a button. But, since we added the button role, it appears to be a button to screen readers even in this case when it is not.</p> <p>Second, even though we have added the button role, I'm not entirely convinced that this makes the element <em>exactly</em> the same as though it were an actual button. Even though user agents and screen readers <em>should</em> treat it as a button, <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_button_role#Possible_effects_on_user_agents_and_assistive_technology">there is not guarantee that they do</a>.</p> <h2 id="what-s-the-solution" tabindex="-1">What's the Solution? <a class="header-anchor" href="https://bitsofco.de/anchors-vs-buttons/">#</a></h2> <p>We have a dilemma.</p> <blockquote> <p>The <code>&lt;button&gt;</code> is semantically correct, but not progressively enhancive, and the <code>&lt;a&gt;</code> is progressively enhancive but not semantically correct.</p> </blockquote> <p>Therefore, the only solution I see is to <em>change the element used based on the circumstance</em>. If we are going to have to use JavaScript to bind the <code>space</code> key event to an <code>&lt;a role=&quot;button&quot;&gt;</code>, we might as well go all the way for semantic consistency. When there is no Javascript, the element remains as a regular link semantically. But when there is Javascript, the anchor element is swapped for a button element instead.</p> <p>Here's how it would work. The element defined in the markup will be a regular link, unmodified.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>#navigation-alternative<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>anchorButton<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Navigation<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span></code></pre> <p>If Javascript is enabled, we can then change all the anchor elements to buttons.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> <span class="token function-variable function">changeAnchorToButton</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">anchor</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Create button element with same content and attributes as anchor</span><br> <span class="token keyword">var</span> button <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">'button'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> button<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'type'</span><span class="token punctuation">,</span> <span class="token string">'button'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> button<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> anchor<span class="token punctuation">.</span>innerHTML<span class="token punctuation">;</span><br> button<span class="token punctuation">.</span>id <span class="token operator">=</span> anchor<span class="token punctuation">.</span>id<span class="token punctuation">;</span><br> <span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> anchor<span class="token punctuation">.</span>classList<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> button<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>anchor<span class="token punctuation">.</span>classList<span class="token punctuation">[</span>i<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 comment">// Change anchor element to button element</span><br> <span class="token keyword">var</span> anchorParent <span class="token operator">=</span> anchor<span class="token punctuation">.</span>parentNode<span class="token punctuation">;</span><br> anchorParent<span class="token punctuation">.</span><span class="token function">replaceChild</span><span class="token punctuation">(</span>button<span class="token punctuation">,</span> anchor<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><span class="token keyword">var</span> anchorButtons <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">'anchorButton'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> anchorButtons<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 function">changeAnchorToButton</span><span class="token punctuation">(</span> anchorButtons<span class="token punctuation">[</span>i<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 style the <code>.anchorButton</code> element so both the<code>&lt;a&gt;</code> and <code>&lt;button&gt;</code> look the same visually -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.anchorButton</span> <span class="token punctuation">{</span><br> <span class="token comment">/* Resets */</span><br> <span class="token property">font-family</span><span class="token punctuation">:</span> sans-serif<span class="token punctuation">;</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 16px<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 property">border</span><span class="token punctuation">:</span> 1px solid #000<span class="token punctuation">;</span><br> <span class="token property">border-radius</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> transparent<span class="token punctuation">;</span><br><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">height</span><span class="token punctuation">:</span> auto<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">cursor</span><span class="token punctuation">:</span> pointer<span class="token punctuation">;</span><br><br> <span class="token comment">/* More styles... */</span><br><span class="token punctuation">}</span><br><span class="token selector">a.anchorButton</span> <span class="token punctuation">{</span><br> <span class="token comment">/* Styles for when it's an &lt;a> */</span><br> <span class="token property">text-decoration</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><span class="token selector">button.anchorButton</span> <span class="token punctuation">{</span><br> <span class="token comment">/* Styles for when it's a &lt;button> */</span><br> <span class="token property">-webkit-appearance</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br> <span class="token property">-moz-appearance</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Although this may be a bit radical, it's the only solution I could come up with that works visually, semantically, and accessibility-wise. And, if we style the <code>.anchorButton</code> element correctly, adding this snippet shouldn't affect the visual look of the page at all.</p> Making Messenger Hunt - An Introduction to Messenger Bots 2016-05-31T00:00:00Z https://bitsofco.de/making-messengerhunt-an-introduction-to-messenger-bots-2/ <p>Since Facebook announced the introduction of bots on Messenger, I knew I had to try making one myself (as I'm <em>a bit</em> obsessed with the idea of bots). So, I decided to make Messenger Hunt, a Messenger bot for Product Hunt.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/ZJLfUGfmdT-350.avif 350w"><source type="image/webp" srcset="https://bitsofco.de/img/ZJLfUGfmdT-350.webp 350w"><img alt="Demo of MessengerHunt 1" loading="lazy" decoding="async" src="https://bitsofco.de/img/ZJLfUGfmdT-350.png" width="350" height="624"></picture> <picture><source type="image/avif" srcset="https://bitsofco.de/img/lptF3BKti2-350.avif 350w"><source type="image/webp" srcset="https://bitsofco.de/img/lptF3BKti2-350.webp 350w"><img alt="Demo of MessengerHunt 2" loading="lazy" decoding="async" src="https://bitsofco.de/img/lptF3BKti2-350.png" width="350" height="624"></picture> <picture><source type="image/avif" srcset="https://bitsofco.de/img/LiSC06dwUt-350.avif 350w"><source type="image/webp" srcset="https://bitsofco.de/img/LiSC06dwUt-350.webp 350w"><img alt="Demo of MessengerHunt 3" loading="lazy" decoding="async" src="https://bitsofco.de/img/LiSC06dwUt-350.png" width="350" height="628"></picture></p> <p><a href="https://m.me/246322469053918">Message the Bot</a> | <a href="https://github.com/ireade/messenger-hunt">Source Code</a></p> <p>Here is how I worked with the Messenger API to create the bot. This article if going to be a bit similar to my &quot;<a href="https://bitsofco.de/creating-khaledbot-an-introduction-to-slack-bots">An Introduction to Slack Bots</a>&quot; as I'm using the same framework, Botkit to develop it.</p> <h2 id="the-setup" tabindex="-1">The Setup <a class="header-anchor" href="https://bitsofco.de/making-messengerhunt-an-introduction-to-messenger-bots-2/">#</a></h2> <p>The setup process for Messenger Bots can be a bit complicated. Because the Messenger API requires a callback URL hosted on a secure server, we can't work locally alone. We have to start live. I use (and recommend) <a href="https://beepboophq.com/">BeepBoop</a> for hosting bots. They are really easy to use and reliable. These are the steps to setting up a new Messenger bot being hosted through BeepBoop.</p> <p><strong>Step 1.</strong> Create a new Node.js (with Botkit) project. Here is a simple boilerplate code to get started -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> Botkit <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'botkit'</span><span class="token punctuation">)</span><br><br><span class="token keyword">var</span> accessToken <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">FACEBOOK_PAGE_ACCESS_TOKEN</span><br><span class="token keyword">var</span> verifyToken <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">FACEBOOK_VERIFY_TOKEN</span><br><span class="token keyword">var</span> port <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">PORT</span><br><br><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>accessToken<span class="token punctuation">)</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'FACEBOOK_PAGE_ACCESS_TOKEN is required but missing'</span><span class="token punctuation">)</span><br><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>verifyToken<span class="token punctuation">)</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'FACEBOOK_VERIFY_TOKEN is required but missing'</span><span class="token punctuation">)</span><br><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>port<span class="token punctuation">)</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'PORT is required but missing'</span><span class="token punctuation">)</span><br><br><span class="token keyword">var</span> controller <span class="token operator">=</span> Botkit<span class="token punctuation">.</span><span class="token function">facebookbot</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">access_token</span><span class="token operator">:</span> accessToken<span class="token punctuation">,</span><br> <span class="token literal-property property">verify_token</span><span class="token operator">:</span> verifyToken<br><span class="token punctuation">}</span><span class="token punctuation">)</span><br><br><span class="token keyword">var</span> bot <span class="token operator">=</span> controller<span class="token punctuation">.</span><span class="token function">spawn</span><span class="token punctuation">(</span><span class="token punctuation">)</span><br><br>controller<span class="token punctuation">.</span><span class="token function">setupWebserver</span><span class="token punctuation">(</span>port<span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> webserver</span><span class="token punctuation">)</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> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><br> controller<span class="token punctuation">.</span><span class="token function">createWebhookEndpoints</span><span class="token punctuation">(</span>webserver<span class="token punctuation">,</span> bot<span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</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">'Ready'</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></code></pre> <p><strong>Step 2.</strong> Push to a Github repository and connect with BeepBoop. If you don't already have an account with BeepBoop you'll need to <a href="https://beepboophq.com/signup">create one</a>.</p> <p><strong>Step 3.</strong> Enable the bot as a Facebook Messenger bot in BeepBoop's Project Settings.</p> <p><strong>Step 4.</strong> Generate a random key to use as the Verify Token (you can use the <a href="https://randomkeygen.com/">Random Key Generator</a>) and paste it under the bot project settings.</p> <p><strong>Step 5.</strong> Get the Base Callback URL from the project settings. Note that this is only the base, the actual callback URL we will give to facebook will have the suffix <code>/facebook/receive</code>.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/XeOyLx9wk3-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/XeOyLx9wk3-780.webp 780w"><img alt="IBeepBoop Messenger Bot Settings" loading="lazy" decoding="async" src="https://bitsofco.de/img/XeOyLx9wk3-780.png" width="780" height="474"></picture></p> <p><strong>Step 6.</strong> Create a new <a href="https://www.facebook.com/pages/create">Facebook Page</a>, or use an existing one. This is the page where people will interact with your bot.</p> <p><strong>Step 7.</strong> Create a new <a href="https://developers.facebook.com/quickstarts/?platform=web">Facebook Application</a>.</p> <p><strong>Step 8.</strong>. Enable &quot;Messenger&quot; for the application. You can find this under &quot;Product Settings&quot;.</p> <p><strong>Step 9.</strong> Setup a New Webhook using the Callback Url from BeepBoop (step 5) and the Verify Token (step 4). Make sure to subscribe to the four fields needed (<code>message_deliveries</code>, <code>messages</code>, <code>messaging_optins</code>, <code>messaging_postbacks</code>).</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/9B0mZZs0yC-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/9B0mZZs0yC-780.webp 780w"><img alt="Webhook Setup" loading="lazy" decoding="async" src="https://bitsofco.de/img/9B0mZZs0yC-780.png" width="780" height="351"></picture></p> <p><strong>Step 10.</strong> Under the &quot;Messenger&quot; section, generate an Access Token for the Page and save it under the BeepBoop project settings.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/N0AEBGfGCh-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/N0AEBGfGCh-780.webp 780w"><img alt="Page Access Token Generated" loading="lazy" decoding="async" src="https://bitsofco.de/img/N0AEBGfGCh-780.png" width="780" height="273"></picture></p> <p><strong>Step 11.</strong> Restart the bot.</p> <h2 id="listening-for-events-and-keywords" tabindex="-1">Listening for Events and Keywords <a class="header-anchor" href="https://bitsofco.de/making-messengerhunt-an-introduction-to-messenger-bots-2/">#</a></h2> <p>Next, we can start having the bot interact with users. Similar to Slack bots, a Messenger bot can respond to events that happen, e.g. when a user clicks a <a href="https://developers.facebook.com/docs/messenger-platform/implementation#send_to_messenger_plugin">Send-to-Messenger</a> button, or to specific keywords.</p> <h3 id="listening-for-events" tabindex="-1">Listening for Events <a class="header-anchor" href="https://bitsofco.de/making-messengerhunt-an-introduction-to-messenger-bots-2/">#</a></h3> <p>There are four events a Messenger bot can listen for -</p> <ul> <li><code>message_received</code> - When a user sends a message to the bot</li> <li><code>facebook_postback</code> - When a user clicks a postback button provided by the bot</li> <li><code>message_delivered</code> - When it is confirmed that the user has received a message sent by the bot</li> <li><code>facebook_optin</code> - When a user clicks a <a href="https://developers.facebook.com/docs/messenger-platform/implementation#send_to_messenger_plugin">Send-to-Messenger</a> button</li> </ul> <p>Using this, we can send a welcome message when a user first adds the bot to Messenger -</p> <pre class="language-js" tabindex="0"><code class="language-js">controller<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">"facebook_optin"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">bot<span class="token punctuation">,</span> message</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Send welcome message</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p>We can also listen for postbacks triggered when a user clicks a button -</p> <pre class="language-js" tabindex="0"><code class="language-js">controller<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'facebook_postback'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">bot<span class="token punctuation">,</span> message</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> message<span class="token punctuation">.</span>payload <span class="token operator">===</span> <span class="token string">"button_postback_name"</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Do stuff related to this button</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <h3 id="listening-for-keywords" tabindex="-1">Listening for Keywords <a class="header-anchor" href="https://bitsofco.de/making-messengerhunt-an-introduction-to-messenger-bots-2/">#</a></h3> <p>We can also listen for specific keywords from a user’s message with the <code>controller.hears</code> method. Unlike Slack bots in which we can listen for keywords from a variety of contexts, there is only one context for Messenger bots, <code>message_received</code>.</p> <pre class="language-js" tabindex="0"><code class="language-js">controller<span class="token punctuation">.</span><span class="token function">hears</span><span class="token punctuation">(</span>array_of_keywords<span class="token punctuation">,</span> <span class="token string">"message_received"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">bot<span class="token punctuation">,</span> message</span><span class="token punctuation">)</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></code></pre> <p>With Messenger Hunt, for example, it listens for the mention of any of the categories on Product Hunt -</p> <pre class="language-js" tabindex="0"><code class="language-js">controller<span class="token punctuation">.</span><span class="token function">hears</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'podcasts'</span><span class="token punctuation">,</span> <span class="token string">'podcast'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string">"message_received"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">bot<span class="token punctuation">,</span> message</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Get products from the Podcasts category</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><br>controller<span class="token punctuation">.</span><span class="token function">hears</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'games'</span><span class="token punctuation">,</span> <span class="token string">'game'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string">"message_received"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">bot<span class="token punctuation">,</span> message</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Get products from the Games category</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><br><span class="token comment">// And so on</span></code></pre> <h2 id="responding-to-events" tabindex="-1">Responding to Events <a class="header-anchor" href="https://bitsofco.de/making-messengerhunt-an-introduction-to-messenger-bots-2/">#</a></h2> <p>Messenger bots, like Slack bots, can respond to events using the simple <code>bot.reply</code> method. This method allows the bot to give a simple text reply to any message.</p> <pre class="language-js" tabindex="0"><code class="language-js">controller<span class="token punctuation">.</span><span class="token function">hears</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'hello'</span><span class="token punctuation">,</span> <span class="token string">'hi'</span><span class="token punctuation">,</span> <span class="token string">'hey'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token string">'message_received'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">bot<span class="token punctuation">,</span> message</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> bot<span class="token punctuation">.</span><span class="token function">reply</span><span class="token punctuation">(</span>message<span class="token punctuation">,</span> <span class="token string">"Hi there!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <h3 id="structured-messages" tabindex="-1">Structured Messages <a class="header-anchor" href="https://bitsofco.de/making-messengerhunt-an-introduction-to-messenger-bots-2/">#</a></h3> <p>In addition to the simple reply methods, Messenger bots can send what are called <a href="https://developers.facebook.com/docs/messenger-platform/implementation#structured_messages">Structured Messages</a>. These are messages that follow one of Facebook’s predefined formats and can include attachments such as images and buttons.</p> <p>For example, the Messenger Hunt bot sends products from a particular category as a <strong>Generic Template</strong> structured message.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> structured_message <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">attachment</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">type</span><span class="token operator">:</span> <span class="token string">'template'</span><span class="token punctuation">,</span><br> <span class="token literal-property property">payload</span><span class="token operator">:</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">template_type</span><span class="token operator">:</span> <span class="token string">'generic'</span><span class="token punctuation">,</span> <span class="token comment">// The Generic Template Structured Message</span><br> <span class="token literal-property property">elements</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token comment">// Array of products</span><br> <span class="token punctuation">{</span><br> <span class="token string-property property">"title"</span><span class="token operator">:</span> post<span class="token punctuation">.</span>name<span class="token punctuation">,</span><br> <span class="token string-property property">"image_url"</span><span class="token operator">:</span> post<span class="token punctuation">.</span>thumbnail<span class="token punctuation">.</span>image_url<span class="token punctuation">,</span><br> <span class="token string-property property">"subtitle"</span><span class="token operator">:</span> post<span class="token punctuation">.</span>tagline<span class="token punctuation">,</span><br> <span class="token string-property property">"buttons"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token comment">// CTAs for each product</span><br> <span class="token punctuation">{</span><br> <span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"postback"</span><span class="token punctuation">,</span><br> <span class="token string-property property">"payload"</span><span class="token operator">:</span> <span class="token string">"postInfo_"</span><span class="token operator">+</span>post<span class="token punctuation">.</span>id<span class="token punctuation">,</span><br> <span class="token string-property property">"title"</span><span class="token operator">:</span><span class="token string">"More Info"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token string-property property">"type"</span><span class="token operator">:</span><span class="token string">"web_url"</span><span class="token punctuation">,</span><br> <span class="token string-property property">"url"</span><span class="token operator">:</span> post<span class="token punctuation">.</span>redirect_url<span class="token punctuation">,</span><br> <span class="token string-property property">"title"</span><span class="token operator">:</span><span class="token string">"Hunt This"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token string-property property">"type"</span><span class="token operator">:</span><span class="token string">"web_url"</span><span class="token punctuation">,</span><br> <span class="token string-property property">"url"</span><span class="token operator">:</span> post<span class="token punctuation">.</span>discussion_url<span class="token punctuation">,</span><br> <span class="token string-property property">"title"</span><span class="token operator">:</span><span class="token string">"Discuss/Upvote"</span><br> <span class="token punctuation">}</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 operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><br> <span class="token punctuation">]</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><br><br><span class="token comment">// Send the structured message object instead of a simple string</span><br>bot<span class="token punctuation">.</span><span class="token function">reply</span><span class="token punctuation">(</span>message<span class="token punctuation">,</span> structured_message<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>Here is the result -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/YtL_jHKAww-350.avif 350w"><source type="image/webp" srcset="https://bitsofco.de/img/YtL_jHKAww-350.webp 350w"><img alt="MessengerHunt Structured Message" loading="lazy" decoding="async" src="https://bitsofco.de/img/YtL_jHKAww-350.png" width="350" height="624"></picture></p> <h3 id="postbacks" tabindex="-1">Postbacks <a class="header-anchor" href="https://bitsofco.de/making-messengerhunt-an-introduction-to-messenger-bots-2/">#</a></h3> <p>The Messenger Hunt bot, as you may have noticed, is not really conversational. Instead, the user interacts with the bot mainly through clicking buttons that trigger postbacks. In my opinion, this is more useful as it doesn't force the user to learn how to &quot;speak&quot; with the bot. Postbacks allow us to create defined responses a user can give and, instead of the user having to type out those specifically, they can just click a button.</p> <p>For example, when users are presented with the list of products from the Messenger Hunt bot, one of the buttons is an option to get more information about the product.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> buttons <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"postback"</span><span class="token punctuation">,</span><br> <span class="token string-property property">"payload"</span><span class="token operator">:</span> <span class="token string">"postInfo_"</span><span class="token operator">+</span>post<span class="token punctuation">.</span>id<span class="token punctuation">,</span><br> <span class="token string-property property">"title"</span><span class="token operator">:</span><span class="token string">"More Info"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span> <span class="token operator">...</span> <span class="token punctuation">}</span><br><span class="token punctuation">]</span></code></pre> <p>Clicking this button triggers a postback, which we can listen for with the <code>controller.on</code> method and respond accordingly.</p> <pre class="language-js" tabindex="0"><code class="language-js">controller<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">'facebook_postback'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">bot<span class="token punctuation">,</span> message</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> message<span class="token punctuation">.</span>payload<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">'postInfo_'</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token operator">-</span><span class="token number">1</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">var</span> postID <span class="token operator">=</span> message<span class="token punctuation">.</span>payload<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">"_"</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Call function to get more information about the product</span><br> <span class="token function">getPostInfo</span><span class="token punctuation">(</span>bot<span class="token punctuation">,</span> message<span class="token punctuation">,</span> postID<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token comment">// Handle other postbacks below</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <h2 id="everything-else" tabindex="-1">Everything Else <a class="header-anchor" href="https://bitsofco.de/making-messengerhunt-an-introduction-to-messenger-bots-2/">#</a></h2> <p>Those are the basics of working with Messenger bots. Everything else is to do with the logic of the particular bot and what it does. If you're interested in seeing the logic behind Messenger Hunt, you can checkout out the full source code at my repository, <a href="https://github.com/ireade/messenger-hunt">ireade/messenger-hunt</a>.</p> <p>You can also <a href="https://m.me/246322469053918">chat with Messenger Hunt</a> or discuss/vote on it on <a href="https://www.producthunt.com/tech/messenger-hunt">Product Hunt</a>.</p> The box-shadow Property 2016-05-24T00:00:00Z https://bitsofco.de/the-box-shadow-property/ <p>The <code>box-shadow</code> property is a CSS3 property that allows us to create shadow effects on almost any element, similar to the drop shadows we can create in design software. These shadow effects allow us to create the illusion of depth in an otherwise flat, 2-dimensional-looking page.</p> <h2 id="the-syntax" tabindex="-1">The Syntax <a class="header-anchor" href="https://bitsofco.de/the-box-shadow-property/">#</a></h2> <p>The <code>box-shadow</code> property accepts a value made up of up to 5 different parts.</p> <pre><code>box-shadow: offset-x offset-y blur spread color position; </code></pre> <p>Unlike other properties, such as <code>border</code>, in which the parts are broken up into sub-properties, the box-shadow property stands alone. This means that it is even more important to take note of the order in which the parts are stated, particularly the length values.</p> <h3 id="offset-x" tabindex="-1">offset-x <a class="header-anchor" href="https://bitsofco.de/the-box-shadow-property/">#</a></h3> <p>The first length value specified is the horizontal offset of the shadow, i.e. the position of the shadow on the x-axis. Positive values push the position of the shadow to the <strong>right</strong> of the element, whereas negative values push the position of the shadow to the <strong>left</strong> of the element.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.left</span> <span class="token punctuation">{</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 20px 0px 10px 0px <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.5<span class="token punctuation">)</span> <span class="token punctuation">}</span><br><br><span class="token selector">.right</span> <span class="token punctuation">{</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> -20px 0px 10px 0px <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.5<span class="token punctuation">)</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/4LFjmS7wKH-812.avif 812w"><source type="image/webp" srcset="https://bitsofco.de/img/4LFjmS7wKH-812.webp 812w"><img alt="Demo of offset-x positive and negative values" loading="lazy" decoding="async" src="https://bitsofco.de/img/4LFjmS7wKH-812.png" width="812" height="343"></picture></p> <h3 id="offset-y" tabindex="-1">offset-y <a class="header-anchor" href="https://bitsofco.de/the-box-shadow-property/">#</a></h3> <p>The second length value specified is the vertical offset of the shadow, i.e. the position of the shadow on the y-axis. Positive values push the position of the shadow <strong>below</strong> the element and negative values push the position of the shadow <strong>above</strong> the element.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.left</span> <span class="token punctuation">{</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 20px 10px 0px <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.5<span class="token punctuation">)</span> <span class="token punctuation">}</span><br><br><span class="token selector">.right</span> <span class="token punctuation">{</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px -20px 10px 0px <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.5<span class="token punctuation">)</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/23cFZ_2gWW-814.avif 814w"><source type="image/webp" srcset="https://bitsofco.de/img/23cFZ_2gWW-814.webp 814w"><img alt="Demo of offset-y positive and negative values" loading="lazy" decoding="async" src="https://bitsofco.de/img/23cFZ_2gWW-814.png" width="814" height="344"></picture></p> <h3 id="blur" tabindex="-1">blur <a class="header-anchor" href="https://bitsofco.de/the-box-shadow-property/">#</a></h3> <p>The third length value given represents the blur radius of the shadow, for example what you would get using a <a href="https://en.wikipedia.org/wiki/Gaussian_blur">Gaussian blur</a> filter in design software. A value of <code>0</code> means that the shadow is completely solid and sharp with no blur at all. The larger the blur value, the less sharp and more hazy/blurry the shadow. Negative values are not allowed and will result in a value of <code>0</code>.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.left</span> <span class="token punctuation">{</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 0px 0px 0px <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.5<span class="token punctuation">)</span> <span class="token punctuation">}</span><br><br><span class="token selector">.middle</span> <span class="token punctuation">{</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 0px 20px 0px <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.5<span class="token punctuation">)</span> <span class="token punctuation">}</span><br><br><span class="token selector">.right</span> <span class="token punctuation">{</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 0px 50px 0px <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.5<span class="token punctuation">)</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/BHKEVK3Bz0-806.avif 806w"><source type="image/webp" srcset="https://bitsofco.de/img/BHKEVK3Bz0-806.webp 806w"><img alt="Demo of blur value at 0px, 20px and 50px" loading="lazy" decoding="async" src="https://bitsofco.de/img/BHKEVK3Bz0-806.png" width="806" height="344"></picture></p> <h3 id="spread" tabindex="-1">spread <a class="header-anchor" href="https://bitsofco.de/the-box-shadow-property/">#</a></h3> <p>The fourth and final length value given represents the size of the shadow. This value can also be thought of us the distance of the shadow from the element. Positive values will <strong>extend</strong> the shadow outside the element in all directions by the amount specified. Negative values will <strong>shrink</strong> the shadow smaller than the element size. The default value of <code>0</code> will keep the shadow spread the same size as the element.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.left</span> <span class="token punctuation">{</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 0px 20px 0px <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.5<span class="token punctuation">)</span> <span class="token punctuation">}</span><br><br><span class="token selector">.middle</span> <span class="token punctuation">{</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 0px 20px 20px <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.5<span class="token punctuation">)</span> <span class="token punctuation">}</span><br><br><span class="token selector">.right</span> <span class="token punctuation">{</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 50px 20px -20px <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.5<span class="token punctuation">)</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/thpo3GUhXL-807.avif 807w"><source type="image/webp" srcset="https://bitsofco.de/img/thpo3GUhXL-807.webp 807w"><img alt="Demo of spread value at 0px, 20px and -20px" loading="lazy" decoding="async" src="https://bitsofco.de/img/thpo3GUhXL-807.png" width="807" height="346"></picture></p> <h3 id="color" tabindex="-1">color <a class="header-anchor" href="https://bitsofco.de/the-box-shadow-property/">#</a></h3> <p>A colour value given represents, as you would expect, the colour of the shadow. It can be any colour unit (see <a href="https://bitsofco.de/working-with-colour-in-css">Working with Colour in CSS</a>).</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.left</span> <span class="token punctuation">{</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 0px 20px 10px #67b3dd <span class="token punctuation">}</span><br><br><span class="token selector">.right</span> <span class="token punctuation">{</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 0px 20px 10px <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.5<span class="token punctuation">)</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/QWQruHN3HY-813.avif 813w"><source type="image/webp" srcset="https://bitsofco.de/img/QWQruHN3HY-813.webp 813w"><img alt="Demo of color value" loading="lazy" decoding="async" src="https://bitsofco.de/img/QWQruHN3HY-813.png" width="813" height="339"></picture></p> <h3 id="position" tabindex="-1">position <a class="header-anchor" href="https://bitsofco.de/the-box-shadow-property/">#</a></h3> <p>The final part of the <code>box-shadow</code> property value is an optional keyword, which represents the position of the shadow. By default, this value is not given, which means that the shadow is an <strong>outer box-shadow</strong>. We can make the shadow an <strong>inner box shadow</strong> by using the <code>inset</code> keyword.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.left</span> <span class="token punctuation">{</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 20px 20px 10px 0px <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.5<span class="token punctuation">)</span> inset <span class="token punctuation">}</span><br><br><span class="token selector">.right</span> <span class="token punctuation">{</span> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 20px 20px 10px 0px <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.5<span class="token punctuation">)</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Jik7u1y1sK-814.avif 814w"><source type="image/webp" srcset="https://bitsofco.de/img/Jik7u1y1sK-814.webp 814w"><img alt="Demo of position values as inset of default" loading="lazy" decoding="async" src="https://bitsofco.de/img/Jik7u1y1sK-814.png" width="814" height="344"></picture></p> <h3 id="multiple-shadows" tabindex="-1">Multiple Shadows <a class="header-anchor" href="https://bitsofco.de/the-box-shadow-property/">#</a></h3> <p>The <code>box-shadow</code> property is able to accept multiple shadows on a single element. Each shadow is added to the box-shadow property as a comma-separated list.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 20px 20px 10px 0px <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.5<span class="token punctuation">)</span> inset<span class="token punctuation">,</span> <span class="token comment">/* inner shadow */</span><br> 20px 20px 10px 0px <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.5<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* outer shadow */</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/gbepxzh6aP-818.avif 818w"><source type="image/webp" srcset="https://bitsofco.de/img/gbepxzh6aP-818.webp 818w"><img alt="Demo of multiple box shadows" loading="lazy" decoding="async" src="https://bitsofco.de/img/gbepxzh6aP-818.png" width="818" height="344"></picture></p> <h3 id="rounded-shadows" tabindex="-1">Rounded Shadows <a class="header-anchor" href="https://bitsofco.de/the-box-shadow-property/">#</a></h3> <p>The <code>box-shadow</code> property's border-radius is controlled by the <code>border-radius</code> property on the same element.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 20px 20px 10px 0px <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.5<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">border-radius</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/8fR6yHGUcx-817.avif 817w"><source type="image/webp" srcset="https://bitsofco.de/img/8fR6yHGUcx-817.webp 817w"><img alt="Demo of rounded shadow" loading="lazy" decoding="async" src="https://bitsofco.de/img/8fR6yHGUcx-817.png" width="817" height="346"></picture></p> <h2 id="putting-it-all-together" tabindex="-1">Putting it all together <a class="header-anchor" href="https://bitsofco.de/the-box-shadow-property/">#</a></h2> <p>Putting all of these parts together, we can create some amazing effects using the <code>box-shadow</code> property.</p> <h3 id="an-alternative-non-layout-blocking-border" tabindex="-1">An Alternative Non-Layout-Blocking Border <a class="header-anchor" href="https://bitsofco.de/the-box-shadow-property/">#</a></h3> <p>We can use the <code>box-shadow</code> property to create a border around an element that doesn't interfere with the box model or the rest of the layout of the page. Further, using the ability to have multiple shadows, we can have different borders on different sides of the element.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.simple</span> <span class="token punctuation">{</span><br> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 0px 0px 40px indianred<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">.multiple</span> <span class="token punctuation">{</span><br> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 20px 20px 0px 20px lightcoral<span class="token punctuation">,</span><br> -20px -20px 0px 20px mediumvioletred<span class="token punctuation">,</span><br> 0px 0px 0px 40px <span class="token function">rgb</span><span class="token punctuation">(</span>200<span class="token punctuation">,</span>200<span class="token punctuation">,</span>200<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/ijel1MmpHv-808.avif 808w"><source type="image/webp" srcset="https://bitsofco.de/img/ijel1MmpHv-808.webp 808w"><img alt="Border using the box-shadow property" loading="lazy" decoding="async" src="https://bitsofco.de/img/ijel1MmpHv-808.png" width="808" height="342"></picture></p> <h3 id="pop-up-effect" tabindex="-1">Pop-Up Effect <a class="header-anchor" href="https://bitsofco.de/the-box-shadow-property/">#</a></h3> <p>Using transforms on the <code>box-shadow</code> (&amp; <code>transform</code>) property, we can create the illusion of an element moving closer or further away from the user.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.popup</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>1<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 0px 10px 5px <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.3<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transition</span><span class="token punctuation">:</span> box-shadow 0.5s<span class="token punctuation">,</span> transform 0.5s<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><span class="token selector">.popup:hover</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">scale</span><span class="token punctuation">(</span>1.3<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 0px 50px 10px <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.3<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transition</span><span class="token punctuation">:</span> box-shadow 0.5s<span class="token punctuation">,</span> transform 0.5s<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/YUq92-gn4Z-820.avif 820w"><source type="image/gif" srcset="https://bitsofco.de/img/YUq92-gn4Z-820.gif 820w"><img alt="Pop-up effect using the box-shadow property" loading="lazy" decoding="async" src="https://bitsofco.de/img/YUq92-gn4Z-820.webp" width="820" height="326"></picture></p> <h3 id="floating-effect" tabindex="-1">Floating Effect <a class="header-anchor" href="https://bitsofco.de/the-box-shadow-property/">#</a></h3> <p>We can add a <code>box-shadow</code> to the <code>:after</code> pseudo-element as well. We can use this to create a shadow below the element, giving it the illusion of being lifted up or dropped down.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.floating</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 property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>0<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transition</span><span class="token punctuation">:</span> transform 1s<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><span class="token selector">.floating:after</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">display</span><span class="token punctuation">:</span> block<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">bottom</span><span class="token punctuation">:</span> -30px<span class="token punctuation">;</span><br> <span class="token property">left</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 8px<span class="token punctuation">;</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0px 0px 15px 0px <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">border-radius</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> <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.2<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transition</span><span class="token punctuation">:</span> transform 1s<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token comment">/* Hover States */</span><br><span class="token selector">.floating:hover</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>-40px<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transition</span><span class="token punctuation">:</span> transform 1s<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><span class="token selector">.floating:hover:after</span> <span class="token punctuation">{</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> 40px<span class="token punctuation">)</span> <span class="token function">scale</span><span class="token punctuation">(</span>0.75<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transition</span><span class="token punctuation">:</span> transform 1s<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/lTuaqFdHZF-812.avif 812w"><source type="image/gif" srcset="https://bitsofco.de/img/lTuaqFdHZF-812.gif 812w"><img alt="Floating effect using the box-shadow property" loading="lazy" decoding="async" src="https://bitsofco.de/img/lTuaqFdHZF-812.webp" width="812" height="328"></picture></p> <p>There are many other effects we can achieve with the <code>box-shadow</code> property. For example, <a href="https://codepen.io/haibnu/pen/FxGsI">this popular pen</a> uses the property to create 8 paper-like effects. Even though it is ostensibly a tool for creating simple drop shadows, it can be a lot more powerful that that alone.</p> Using Jekyll for Rapid CSS Testing 2016-05-17T00:00:00Z https://bitsofco.de/using-jekyll-for-rapid-css-testing/ <p>Last week, I gave a talk at <a href="https://jekyllconf.com/">JekyllConf</a>, the free online global conference for all things Jekyll. My talk, &quot;<a href="https://www.youtube.com/watch?v=PRKV5IGKF2c&amp;feature=youtu.be">Using Jekyll for Rapid CSS Testing</a>&quot;, was about how I use Jekyll to quickly and easily create visual demos of CSS properties and their potential values. Using this method, I created <a href="https://cheatsheets.bitsofco.de">cheatsheets.bitsofco.de</a>, where I display the resulting demos. Here's an example of the cheatsheet for Flexbox -</p> <p><a href="https://cheatsheets.bitsofco.de/flexbox/"><picture><source type="image/avif" srcset="https://bitsofco.de/img/YrwRkt9urf-780.avif 780w"><source type="image/gif" srcset="https://bitsofco.de/img/YrwRkt9urf-780.gif 780w"><img alt="The Cheatsheet for Flexbox" loading="lazy" decoding="async" src="https://bitsofco.de/img/YrwRkt9urf-780.webp" width="780" height="549"></picture></a></p> <p>You can watch the <a href="https://www.youtube.com/watch?v=PRKV5IGKF2c&amp;feature=youtu.be">full talk here</a>, but here is an overview of the method I used to create the site.</p> <h2 id="setting-up-the-jekyll-site" tabindex="-1">Setting up the Jekyll Site <a class="header-anchor" href="https://bitsofco.de/using-jekyll-for-rapid-css-testing/">#</a></h2> <p>The Cheatsheets site uses Jekyll in a rather unconventional way. There are no blog posts, as is usually expected and instead we have “cheatsheets” (e.g. the cheatsheet for <a href="https://cheatsheets.bitsofco.de/flexbox/">Flexbox</a> or <a href="https://cheatsheets.bitsofco.de/object-fit-position/">object-fit &amp; object-position</a>).</p> <p>If we want to use Jekyll for groups of content other than blog posts or pages, we can use what is called a <strong>custom collection</strong>. To create a custom collection, we define it in the <code>_config.yml</code> file. This is how the cheatsheets custom collection is defined -</p> <pre class="language-yaml" tabindex="0"><code class="language-yaml"><span class="token key atrule">collections</span><span class="token punctuation">:</span><br> <span class="token key atrule">cheatsheets</span><span class="token punctuation">:</span> <span class="token comment"># The name of the custom collection</span><br> <span class="token key atrule">output</span><span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token comment"># Makes the collection public</span><br> <span class="token key atrule">permalink</span><span class="token punctuation">:</span> /<span class="token punctuation">:</span>path/ <span class="token comment"># Sets the permalink structure</span></code></pre> <p>We can also specify default front matter for custom collections. For example, instead of having to set the layout for each cheatsheet in the front matter of each file, we can set this as the default behaviour in the <code>_config.yml</code> file.</p> <pre class="language-yaml" tabindex="0"><code class="language-yaml"><span class="token key atrule">defaults</span><span class="token punctuation">:</span><br> <span class="token punctuation">-</span><br> <span class="token key atrule">scope</span><span class="token punctuation">:</span> <span class="token comment"># Sets the scope of the defaults we are about to set to cheatsheets collection</span><br> <span class="token key atrule">type</span><span class="token punctuation">:</span> <span class="token string">"cheatsheets"</span><br> <span class="token key atrule">values</span><span class="token punctuation">:</span> <span class="token comment"># The default values</span><br> <span class="token key atrule">layout</span><span class="token punctuation">:</span> <span class="token string">"cheatsheet"</span></code></pre> <p>When a custom collection is created, Jekyll expects the files within that collection to be stored in a directory titled after the name of the collection. So, the files within the cheatsheets collection should be in a directory titled <code>_cheatsheets</code>.</p> <p>The resulting directory for the Cheatsheets Jekyll site looks like this -</p> <pre><code>- _cheatsheets/ - _data/ - _includes/ - _layouts/ - _sass/ - _site/ - css/ - js/ - _config.yml - index.html </code></pre> <h2 id="creating-a-cheatsheet" tabindex="-1">Creating a Cheatsheet <a class="header-anchor" href="https://bitsofco.de/using-jekyll-for-rapid-css-testing/">#</a></h2> <p>Once the Jekyll site is setup, we can start creating the actual cheatsheet files. There are 3 files involved to create a cheatsheet.</p> <h3 id="1-the-data" tabindex="-1">1. The Data <a class="header-anchor" href="https://bitsofco.de/using-jekyll-for-rapid-css-testing/">#</a></h3> <p>Every cheatsheet has a data file, a YAML file stored in the <code>_data</code> directory (e.g. <code>_data/DATA_NAME.yml</code>). This is where the CSS properties and their values are stored. The file contains an array of <code>properties</code>, each with its own array of <code>values</code>.</p> <pre class="language-yaml" tabindex="0"><code class="language-yaml"><span class="token key atrule">properties</span><span class="token punctuation">:</span><br><span class="token punctuation">-</span> <span class="token punctuation">{</span><br> <span class="token key atrule">property</span><span class="token punctuation">:</span> <span class="token string">'PROPERTY_NAME'</span><span class="token punctuation">,</span><br> <span class="token key atrule">values</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token key atrule">value</span><span class="token punctuation">:</span> <span class="token string">'VALUE_NAME'</span><span class="token punctuation">,</span><br> <span class="token key atrule">default</span><span class="token punctuation">:</span> <span class="token boolean important">true</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token key atrule">value</span><span class="token punctuation">:</span> <span class="token string">'VALUE_NAME'</span><span class="token punctuation">,</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <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> <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><br><span class="token punctuation">-</span> <span class="token punctuation">{</span> <span class="token punctuation">...</span> <span class="token punctuation">}</span></code></pre> <p>Additional information can also be specified here such as a <code>description</code> of the property or value. We can also add custom keys for anything specific to the cheatsheet group. For example, in the cheatsheet for <a href="https://cheatsheets.bitsofco.de/flexbox/">Flexbox</a>, I added a custom key, <code>applies-to</code>, in order to differentiate between properties that apply to the Flex Container versus the Flex Item.</p> <h3 id="2-the-demo" tabindex="-1">2. The Demo <a class="header-anchor" href="https://bitsofco.de/using-jekyll-for-rapid-css-testing/">#</a></h3> <p>This is the actual cheatsheet file, stored in the <code>_cheatsheets</code> directory (e.g., <code>_cheatsheets/CHEATSHEET_NAME.yml</code>). We can link the cheatsheet to the relevant data file from the <code>_data</code> directory by specifying it in the front matter.</p> <pre class="language-html" tabindex="0"><code class="language-html">---<br>title: CHEATSHEET_NAME<br>data_file: DATA_NAME<br>---<br><br><span class="token comment">&lt;!-- Demo will go here --></span></code></pre> <p>Once we have linked the data to the page, we can now create the demos of the properties in the file. In the content of the page, we can loop through each property in the data file and each value for each property.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token comment">&lt;!-- Loop through each property --></span><br></code></pre> <h3 id="3-the-layout" tabindex="-1">3. The Layout <a class="header-anchor" href="https://bitsofco.de/using-jekyll-for-rapid-css-testing/">#</a></h3> <p>This file defines the layout for the cheatsheet page (<code>_layouts/cheatsheet.html</code>). As specified in our <code>_config.yml</code> file, it is the default layout for all files within the cheatsheets collection.</p> <p>In addition to laying out the key elements on the page such as the header and footer, it also creates a table of all the property-value pairs so a complete overview can be seen on one table.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>table</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>thead</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tr</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</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>property<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Property<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</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>description<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Description<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</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>values<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Values<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tr</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>thead</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tbody</span><span class="token punctuation">></span></span><br> <span class="token comment">&lt;!-- Loop through each property --></span><br> <br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tbody</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>table</span><span class="token punctuation">></span></span></code></pre> <h2 id="an-example" tabindex="-1">An Example <a class="header-anchor" href="https://bitsofco.de/using-jekyll-for-rapid-css-testing/">#</a></h2> <p>Here is a very basic example of a cheatsheet for one property, <code>text-align</code>. The data file, <code>_data/text-align.yml</code>, only contains one property, <code>text-align</code>, which has four potential values.</p> <pre class="language-yaml" tabindex="0"><code class="language-yaml"><span class="token key atrule">properties</span><span class="token punctuation">:</span><br><span class="token punctuation">-</span> <span class="token punctuation">{</span><br> <span class="token key atrule">property</span><span class="token punctuation">:</span> <span class="token string">'text-align'</span><span class="token punctuation">,</span><br> <span class="token key atrule">description</span><span class="token punctuation">:</span> <span class="token string">'Describes how inline-level content of a block container is aligned.'</span><span class="token punctuation">,</span><br> <span class="token key atrule">values</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><br> <span class="token punctuation">{</span><br> <span class="token key atrule">value</span><span class="token punctuation">:</span> <span class="token string">'left'</span><span class="token punctuation">,</span><br> <span class="token key atrule">default</span><span class="token punctuation">:</span> <span class="token boolean important">true</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token key atrule">value</span><span class="token punctuation">:</span> <span class="token string">'right'</span><span class="token punctuation">,</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token key atrule">value</span><span class="token punctuation">:</span> <span class="token string">'center'</span><span class="token punctuation">,</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br> <span class="token punctuation">{</span><br> <span class="token key atrule">value</span><span class="token punctuation">:</span> <span class="token string">'justify'</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 cheatsheet file, <code>_cheatsheets/text-align.html</code>, applies the property-value pairs we are testing to a <code>&lt;div&gt;</code> containing a single paragraph -</p> <pre class="language-html" tabindex="0"><code class="language-html">---<br>title: Text-Align<br>data_file: text-align<br>---<br><br><span class="token comment">&lt;!-- Loop through each property --></span><br><br><br><span class="token comment">&lt;!-- Note: I've removed the classes and IDs on the elements for clarity --></span></code></pre> <p>Here's the result we get -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/EYS58TJMMY-898.avif 898w"><source type="image/webp" srcset="https://bitsofco.de/img/EYS58TJMMY-898.webp 898w"><img alt="The Cheatsheet for text-align" loading="lazy" decoding="async" src="https://bitsofco.de/img/EYS58TJMMY-898.png" width="898" height="1164"></picture></p> <p><a href="https://cheatsheets.bitsofco.de/text-align/">Visit this page</a></p> <p>The Cheatsheets site can also handle much more complex situations. For example the <code>object-position</code> property, whose values are not static but can be a range of length units. You can <a href="https://www.youtube.com/watch?v=PRKV5IGKF2c&amp;feature=youtu.be">watch the talk</a> to see how I handle those situations, and how the data file can be modified to allow for them. You can also find the source for the Cheatsheets site at my repository, <a href="https://github.com/ireade/cheatsheets">ireade/cheatsheets</a>.</p> <p>I plan on adding more cheatsheets to the site as I test more CSS properties. If there's anything you'd like to see in particular, let me know.</p> Setting up a Basic Service Worker 2016-05-10T00:00:00Z https://bitsofco.de/setting-up-a-basic-service-worker/ <p><a href="https://www.w3.org/TR/service-workers/">Service Workers</a> are a relatively new, but incredibly powerful, technology. A service worker is a JavaScript file that runs independently to a web page and can perform specific services such as background sync, push notifications, handling network requests, and caching. Because of its independence, it does not have the access to the DOM a typical JavaScript file does, but this allows it to be fully asynchronous and therefore non-blocking.</p> <p>Because service workers can be used in a variety of circumstances for many different tasks, they can get quite complicated. So, to get started, I thought I would show how to setup a basic service worker to handle something simple, caching. I also decided to try something a bit different and made it a video tutorial instead of a regular article. Here it is -</p> <p><a href="https://www.youtube.com/watch?v=BfL3pprhnms"><picture><source type="image/avif" srcset="https://bitsofco.de/img/wgbGt3626z-1280.avif 1280w"><source type="image/webp" srcset="https://bitsofco.de/img/wgbGt3626z-1280.webp 1280w"><img alt="Setting up a Basic Service Worker" loading="lazy" decoding="async" src="https://bitsofco.de/img/wgbGt3626z-1280.png" width="1280" height="720"></picture></a></p> <p>And this was the <code>service-worker.js</code> we setup in the tutorial -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Set a name for the current cache</span><br><span class="token keyword">var</span> cacheName <span class="token operator">=</span> <span class="token string">'v1'</span><span class="token punctuation">;</span><br><br><span class="token comment">// Default files to always cache</span><br><span class="token keyword">var</span> cacheFiles <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token string">'./'</span><span class="token punctuation">,</span><br> <span class="token string">'./index.html'</span><span class="token punctuation">,</span><br> <span class="token string">'./js/app.js'</span><span class="token punctuation">,</span><br> <span class="token string">'./css/reset.css'</span><span class="token punctuation">,</span><br> <span class="token string">'./css/style.css'</span><span class="token punctuation">,</span><br> <span class="token string">'https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,700,400italic,700italic'</span><br><span class="token punctuation">]</span><br><br><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">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><span class="token string">'[ServiceWorker] Installed'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// e.waitUntil Delays the event until the Promise is resolved</span><br> e<span class="token punctuation">.</span><span class="token function">waitUntil</span><span class="token punctuation">(</span><br><br> <span class="token comment">// Open the cache</span><br> caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span>cacheName<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">cache</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Add all the default files to the cache</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'[ServiceWorker] Caching cacheFiles'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> cache<span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>cacheFiles<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 comment">// end e.waitUntil</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><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">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><span class="token string">'[ServiceWorker] Activated'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> e<span class="token punctuation">.</span><span class="token function">waitUntil</span><span class="token punctuation">(</span><br><br> <span class="token comment">// Get all the cache keys (cacheName)</span><br> caches<span class="token punctuation">.</span><span class="token function">keys</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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">cacheNames</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> Promise<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span>cacheNames<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">thisCacheName</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// If a cached item is saved under a previous cacheName</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>thisCacheName <span class="token operator">!==</span> cacheName<span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Delete that cached file</span><br> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'[ServiceWorker] Removing Cached Files from Cache - '</span><span class="token punctuation">,</span> thisCacheName<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> caches<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span>thisCacheName<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><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 comment">// end e.waitUntil</span><br><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><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">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><span class="token string">'[ServiceWorker] Fetch'</span><span class="token punctuation">,</span> e<span class="token punctuation">.</span>request<span class="token punctuation">.</span>url<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// e.respondWidth Responds to the fetch event</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 in cache for the request being made</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><br><br><br> <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// If the request is in the cache</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> response <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">"[ServiceWorker] Found in Cache"</span><span class="token punctuation">,</span> e<span class="token punctuation">.</span>request<span class="token punctuation">.</span>url<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token comment">// Return the cached version</span><br> <span class="token keyword">return</span> response<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token comment">// If the request is NOT in the cache, fetch and cache</span><br><br> <span class="token keyword">var</span> requestClone <span class="token operator">=</span> e<span class="token punctuation">.</span>request<span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token function">fetch</span><span class="token punctuation">(</span>requestClone<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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span>response <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">"[ServiceWorker] No response from fetch "</span><span class="token punctuation">)</span><br> <span class="token keyword">return</span> response<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token keyword">var</span> responseClone <span class="token operator">=</span> response<span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Open the cache</span><br> caches<span class="token punctuation">.</span><span class="token function">open</span><span class="token punctuation">(</span>cacheName<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">cache</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Put the fetched response in the cache</span><br> cache<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>request<span class="token punctuation">,</span> responseClone<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">'[ServiceWorker] New Data Cached'</span><span class="token punctuation">,</span> e<span class="token punctuation">.</span>request<span class="token punctuation">.</span>url<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Return the response</span><br> <span class="token keyword">return</span> response<span class="token punctuation">;</span><br><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// end caches.open</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 keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err</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">'[ServiceWorker] Error Fetching &amp; Caching New Data'</span><span class="token punctuation">,</span> err<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><br> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// end caches.match(e.request)</span><br> <span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// end e.respondWith</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br></code></pre> <p><a href="https://github.com/ireade/boilerplate-service-worker">Full Source Code</a> | <a href="https://ireade.github.io/boilerplate-service-worker/src/">Live Demo</a></p> <p>This is the first video tutorial like this I've done, so excuse my nervousness! I would also love to hear any feedback you have on the video; if you found it helpful or ways I could improve on it.</p> Making IFrames Responsive 2016-04-26T00:00:00Z https://bitsofco.de/iframe-responsive/ <p>While creating the <a href="https://caniuse.bitsofco.de">embed for caniuse</a>, one part I found quite challenging was making the iframe fully responsive. When the iframe is resized, the content within it shifts, and the height that the <code>&lt;iframe&gt;</code> element itself should be, changes. However, because of the <a href="https://en.wikipedia.org/wiki/Same-origin_policy">Content Security Same-origin Policy</a>, there is no way to access the height of the content within the iframe from the document hosting the iframe.</p> <p>With most embedded content, for example a Youtube Video, we can workaround this because, even though the height does change, the aspect ratio stays the same. So, we can <a href="https://www.smashingmagazine.com/2014/02/making-embedded-content-work-in-responsive-design/">use CSS to determine what the height of the iframe should be</a> at any given width. With this caniuse embed, however, there is no standard size so I needed to have access to the actual height of the content within the iframe to determine what the height of the <code>&lt;iframe&gt;</code> element should be.</p> <p>I spent ages looking around for how I could do this and came up short. The solution I eventually came up with was to use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage">postMessage API</a> to communicate between a script in the embedded document, and a script in the hosting document.</p> <p>Although this method is limited in that requires that we have access to both documents, I thought it would still be useful to share just in case someone else is in the same situation. So here is how it works.</p> <h2 id="1-determine-the-height-of-the-iframe-content-on-page-load" tabindex="-1">1. Determine the height of the iframe content on page load <a class="header-anchor" href="https://bitsofco.de/iframe-responsive/">#</a></h2> <p>Firstly, there are two scripts involved here -</p> <ul> <li><code>iframe.js</code> - the script associated with the iframe document</li> <li><code>parent.js</code> - the script associated with the document loading the iframe</li> </ul> <p>In <code>iframe.js</code>, we have to determine the height of the document before we can pass it to the parent document. We do this by getting the <code>scrollHeight</code> of the main element (<code>#main-element</code>) within the document.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> documentHeight <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span>‘main<span class="token operator">-</span>element'<span class="token punctuation">)</span><span class="token punctuation">.</span>scrollHeight<span class="token punctuation">;</span></code></pre> <p>We use the height of the main container element instead of just the <code>body</code> because using the body element will cause an infinite loop when we repeat this on the resize of the window (See step 4).</p> <h2 id="2-pass-the-height-to-the-hosting-document" tabindex="-1">2. Pass the height to the hosting document <a class="header-anchor" href="https://bitsofco.de/iframe-responsive/">#</a></h2> <p>Once we have the height, we can use the postMessage API to send the message to <code>parent.js</code>.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Add some unique identifier to the string being passed</span><br><span class="token keyword">var</span> message <span class="token operator">=</span> <span class="token string">'documentHeight:'</span><span class="token operator">+</span>documentHeight<span class="token punctuation">;</span><br><br><span class="token comment">// Pass message to (any) parent document</span><br>parent<span class="token punctuation">.</span><span class="token function">postMessage</span><span class="token punctuation">(</span>message<span class="token punctuation">,</span> <span class="token string">"*"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br></code></pre> <p>Then, in parent.js, we add an event listener for the message. The method I’m using here is based off an <a href="https://davidwalsh.name/window-iframe">article from David Walsh</a>.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Setup event listener</span><br><span class="token keyword">var</span> eventMethod <span class="token operator">=</span> window<span class="token punctuation">.</span>addEventListener <span class="token operator">?</span> <span class="token string">"addEventListener"</span> <span class="token operator">:</span> <span class="token string">"attachEvent"</span><span class="token punctuation">;</span><br><span class="token keyword">var</span> eventer <span class="token operator">=</span> window<span class="token punctuation">[</span>eventMethod<span class="token punctuation">]</span><span class="token punctuation">;</span><br><span class="token keyword">var</span> messageEvent <span class="token operator">=</span> eventMethod <span class="token operator">==</span> <span class="token string">"attachEvent"</span> <span class="token operator">?</span> <span class="token string">"onmessage"</span> <span class="token operator">:</span> <span class="token string">"message"</span><span class="token punctuation">;</span><br><br><span class="token comment">// Listen for event</span><br><span class="token function">eventer</span><span class="token punctuation">(</span>messageEvent<span class="token punctuation">,</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><br> <span class="token comment">// Check that message being passed is the documentHeight</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> e<span class="token punctuation">.</span>data <span class="token operator">===</span> <span class="token string">'string'</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span>e<span class="token punctuation">.</span>data<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">'documentHeight:'</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Split string from identifier</span><br> <span class="token keyword">var</span> height <span class="token operator">=</span> e<span class="token punctuation">.</span>data<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">'documentHeight:'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br><br> <span class="token comment">// do stuff with the height</span><br><br> <span class="token punctuation">}</span> <br><span class="token punctuation">}</span><span class="token punctuation">,</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <h2 id="3-resize-the-iframe-element-s-height-appropriately" tabindex="-1">3. Resize the <code>&lt;iframe&gt;</code> element's height appropriately <a class="header-anchor" href="https://bitsofco.de/iframe-responsive/">#</a></h2> <p>When we receive the new height from the <code>iframe.js</code>, we can change the height of the <code>&lt;iframe&gt;</code> element in the parent document.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> height <span class="token operator">=</span> e<span class="token punctuation">.</span>data<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">'documentHeight:'</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br> height <span class="token operator">=</span> <span class="token function">parseInt</span><span class="token punctuation">(</span>height<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">30</span><span class="token punctuation">;</span> <span class="token comment">// add a bit of extra space as buffer</span><br><br><span class="token comment">// Change height of &lt;iframe> element</span><br>document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">'myIframe'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>height <span class="token operator">=</span> height <span class="token operator">+</span> <span class="token string">'px'</span><span class="token punctuation">;</span></code></pre> <h2 id="4-repeat-on-resize-of-the-iframe" tabindex="-1">4. Repeat on resize of the iframe <a class="header-anchor" href="https://bitsofco.de/iframe-responsive/">#</a></h2> <p>Back in <code>iframe.js</code>, we can send the new height to <code>parent.js</code> everytime the height of the document changes. Using <code>window.onresize</code>, we can check if the <code>documentHeight</code> has changed significantly (in this case by at least 10px), then send the new height to <code>parent.js</code>.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// On resize of the window, recalculate the height of the main element,</span><br><span class="token comment">// and pass to the parent document again</span><br>window<span class="token punctuation">.</span><span class="token function-variable function">onresize</span> <span class="token operator">=</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 keyword">var</span> newDocumentHeight <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span>‘main<span class="token operator">-</span>element'<span class="token punctuation">)</span><span class="token punctuation">.</span>scrollHeight<span class="token punctuation">;</span><br> <span class="token keyword">var</span> heightDiff <span class="token operator">=</span> documentHeight <span class="token operator">-</span> newDocumentHeight<span class="token punctuation">;</span><br><br> <span class="token comment">// If difference between current height and new height is more than 10px</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> heightDiff <span class="token operator">></span> <span class="token number">10</span> <span class="token operator">|</span> heightDiff <span class="token operator">&lt;</span> <span class="token operator">-</span><span class="token number">10</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> documentHeight <span class="token operator">=</span> newDocumentHeight<span class="token punctuation">;</span><br> message <span class="token operator">=</span> <span class="token string">'documentHeight:'</span><span class="token operator">+</span>documentHeight<span class="token punctuation">;</span><br> parent<span class="token punctuation">.</span><span class="token function">postMessage</span><span class="token punctuation">(</span>message<span class="token punctuation">,</span><span class="token string">"*"</span><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>Once the message is posted to <code>parent.js</code>, the <code>&lt;iframe&gt;</code> element's height will be updated.</p> <p>The reason we don't use the <code>body</code> element to check the height of the iframe content is because, when we resize the <code>&lt;iframe&gt;</code> element in the parent document, that itself will trigger a change in the height of the <code>body</code> element in the iframe document, causing an infinite loop.</p> <p>You can check out a demo of this at <a href="https://demo.bitsofco.de/responsive-iframe">demo.bitsofco.de/responsive-iframe</a>.</p> Initial, Inherit, Unset, and Revert 2016-04-19T00:00:00Z https://bitsofco.de/initial-inherit-unset-and-revert/ <p>There are four values that theoretically can be applied to any CSS property (besides <code>none</code>). These are the explicit defaulting values - <code>initial</code>, <code>inherit</code>, <code>unset</code>, and <code>revert</code>. These values allow us a lot of nuance in setting defaults for properties. Although they are not all universally supported yet, they can be useful to know and understand their differences.</p> <h2 id="initial" tabindex="-1">Initial <a class="header-anchor" href="https://bitsofco.de/initial-inherit-unset-and-revert/">#</a></h2> <p>The <code>initial</code> value represents the default value for the property, as defined by the <a href="https://www.w3.org/TR/CSS/">official CSS Specification</a>. For example for a <code>&lt;p&gt;</code> element, <code>text-align</code> is <code>left</code> and <code>display</code> is <code>inline</code>.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">text-align</span><span class="token punctuation">:</span> initial<span class="token punctuation">;</span><br> <span class="token property">display</span><span class="token punctuation">:</span> initial<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgba</span><span class="token punctuation">(</span>255<span class="token punctuation">,</span>219<span class="token punctuation">,</span>58<span class="token punctuation">,</span> 0.3<span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br> Hello, world!<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/F3LTTAmSgm-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/F3LTTAmSgm-780.webp 780w"><img alt="Example Value Initial" loading="lazy" decoding="async" src="https://bitsofco.de/img/F3LTTAmSgm-780.png" width="780" height="125"></picture></p> <p class="ciu_embed" data-feature="css-initial-value" 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-initial-value.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/css-initial-value.png"> <img src="https://caniuse.bitsofco.de/image/css-initial-value.jpg" alt="Data on support for the css-initial-value feature across the major browsers from caniuse.com"> </picture> </p> <h2 id="inherit" tabindex="-1">Inherit <a class="header-anchor" href="https://bitsofco.de/initial-inherit-unset-and-revert/">#</a></h2> <p>The <code>inherit</code> value represents the value of the element's immediate parent for the same property.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">border</span><span class="token punctuation">:</span> 2px solid plum<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">&lt;</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">border</span><span class="token punctuation">:</span> inherit<span class="token punctuation">;</span> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgba</span><span class="token punctuation">(</span>255<span class="token punctuation">,</span>219<span class="token punctuation">,</span>58<span class="token punctuation">,</span> 0.3<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> Hello, world!<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/ZpaE8sxysn-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/ZpaE8sxysn-780.webp 780w"><img alt="Example Inherit Value" loading="lazy" decoding="async" src="https://bitsofco.de/img/ZpaE8sxysn-780.png" width="780" height="185"></picture></p> <p>If the property is not explicitly defined for the parent element, the behaviour of <code>revert</code> will occur. It will <strong>not</strong> keep travelling up the element's parents to find a value for that property that has been explicitly defined.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">border</span><span class="token punctuation">:</span> 2px solid plum<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">&lt;</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">border</span><span class="token punctuation">:</span> inherit<span class="token punctuation">;</span> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgba</span><span class="token punctuation">(</span>255<span class="token punctuation">,</span>219<span class="token punctuation">,</span>58<span class="token punctuation">,</span> 0.3<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> Hello, world!<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/qmnIZ5ZQ6--780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/qmnIZ5ZQ6--780.webp 780w"><img alt="Example Inherit Value when Parent Element's Property is not defined" loading="lazy" decoding="async" src="https://bitsofco.de/img/qmnIZ5ZQ6--780.png" width="780" height="197"></picture></p> <h2 id="unset" tabindex="-1">Unset <a class="header-anchor" href="https://bitsofco.de/initial-inherit-unset-and-revert/">#</a></h2> <p>The <code>unset</code> value is sort of a combination of <code>initial</code> and <code>inherit</code>.</p> <p>There are some properties that, if not explicitly specified, will default to <code>inherit</code>. For example, if we set the <code>color</code> for an element, it applies to all child elements by default. Whereas other properties, like <code>border</code>, do not inherit by default.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">border</span><span class="token punctuation">:</span> 2px solid plum<span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> cornflowerblue<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">&lt;</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">background-color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>200<span class="token punctuation">,</span> 200<span class="token punctuation">,</span> 200<span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br> Hello, world<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/aXe8pr16QX-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/aXe8pr16QX-780.webp 780w"><img alt="Example showing the default inheritance of the color property but no inheritance of border property" loading="lazy" decoding="async" src="https://bitsofco.de/img/aXe8pr16QX-780.png" width="780" height="196"></picture></p> <p>The <code>color</code> property is inherited but <code>border</code> is not</p> <p>When <code>unset</code> is applied to a property, it will apply either <code>initial</code> or <code>inherit</code>, depending on what the property's default behaviour is. If the property by default inherits, the <code>inherit</code> will be applied. Otherwise, <code>initial</code> will be applied.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">border</span><span class="token punctuation">:</span> 2px solid plum<span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> cornflowerblue<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">&lt;</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">border</span><span class="token punctuation">:</span> unset<span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> unset<span class="token punctuation">;</span> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>200<span class="token punctuation">,</span> 200<span class="token punctuation">,</span> 200<span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br> Hello, world<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/aXe8pr16QX-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/aXe8pr16QX-780.webp 780w"><img alt="Example unset value" loading="lazy" decoding="async" src="https://bitsofco.de/img/aXe8pr16QX-780.png" width="780" height="196"></picture></p> <p>For the <code>border</code> property, the <code>initial</code> value is applied whereas for the <code>color</code> property, the <code>inherit</code> value is applied</p> <p class="ciu_embed" data-feature="css-unset-value" 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-unset-value.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/css-unset-value.png"> <img src="https://caniuse.bitsofco.de/image/css-unset-value.jpg" alt="Data on support for the css-unset-value feature across the major browsers from caniuse.com"> </picture> </p> <h2 id="revert" tabindex="-1">Revert <a class="header-anchor" href="https://bitsofco.de/initial-inherit-unset-and-revert/">#</a></h2> <p>The <code>revert</code> value, previously called <code>default</code>, represents whatever value the property would be if nothing was applied to it at all.</p> <p>If no value is applied to a property in the author stylesheet (the styles we as the webpage authors write), the following steps are taken to find a value -</p> <ol> <li>The <strong>user defined stylesheet</strong> is checked for styles applied to that element.</li> <li>If nothing is found, the <strong>user agent stylesheet</strong> is checked.</li> <li>If nothing is found, the equivalent of <code>unset</code> is applied.</li> </ol> <p>For example, a style frequently added to <code>&lt;div&gt;</code> elements by user agent stylesheets is <code>display: block;</code>, even though the initial value for <code>display</code> is <code>inline</code>. Here is what happens when <code>revert</code> is used compared to <code>initial</code> -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">display</span><span class="token punctuation">:</span> revert<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgba</span><span class="token punctuation">(</span>255<span class="token punctuation">,</span>219<span class="token punctuation">,</span>58<span class="token punctuation">,</span> 0.3<span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br> Hello, world! (revert)<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">display</span><span class="token punctuation">:</span> initial<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgba</span><span class="token punctuation">(</span>255<span class="token punctuation">,</span>219<span class="token punctuation">,</span>58<span class="token punctuation">,</span> 0.3<span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><br> Hello, world! (initial)<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/7boQfsc6yT-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/7boQfsc6yT-780.webp 780w"><img alt="Example Value Revert on P Element" loading="lazy" decoding="async" src="https://bitsofco.de/img/7boQfsc6yT-780.png" width="780" height="231"></picture></p> <p class="ciu_embed" data-feature="css-revert-value" 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-revert-value.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/css-revert-value.png"> <img src="https://caniuse.bitsofco.de/image/css-revert-value.jpg" alt="Data on support for the css-revert-value feature across the major browsers from caniuse.com"> </picture> </p> A Gulp Workflow for Building HTML Email 2016-04-12T00:00:00Z https://bitsofco.de/a-gulp-workflow-for-building-html-email/ <p>Every week when I publish a new article to this site, I use MailChimp to send out an rss-based newsletter with the latest article to people subscribed. I designed and built the template for this newsletter and, as you would expect, it was quite a process. I was initially using Jekyll and going a lot of things manually, but the whole process was just really a mess. So, I have been working on creating a gulp workflow for building HTML emails.</p> <p>I needed this workflow to enable me to do a few things -</p> <ul> <li>Have templating and partials to enable splitting up of the template files</li> <li>Compile SASS files to CSS (optionally include PostCSS for additional features)</li> <li>Inline the compiled CSS</li> <li>Allow for an additional CSS file that is embedded in a <code>&lt;style&gt;</code> element, not inlined</li> <li>Easily test with real data</li> </ul> <p>You can get the <a href="https://github.com/ireade/gulp-email-workflow">workflow files here</a>. It comes with a simple responsive MailChimp template to get started.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/gIZ6lfV8xR-930.avif 930w"><source type="image/webp" srcset="https://bitsofco.de/img/gIZ6lfV8xR-930.webp 930w"><img alt="Sample Email Template" loading="lazy" decoding="async" src="https://bitsofco.de/img/gIZ6lfV8xR-930.png" width="930" height="580"></picture></p> <p>If you are completely new to Gulp, you should check out this <a href="https://bitsofco.de/a-simple-gulp-workflow">Simple Gulp Workflow</a> to give you a quick introduction into how it works.</p> <h2 id="the-folder-structure" tabindex="-1">The Folder Structure <a class="header-anchor" href="https://bitsofco.de/a-gulp-workflow-for-building-html-email/">#</a></h2> <p>Here is how the working directory is structured, including the default files -</p> <pre><code>— gulp-email-workflow/ — src/ — data/ — mailchimp.json — emails/ — index.nunjucks — sass/ — inline.scss — embedded.scss — templates/ — mailchimp.nunjucks — partials/ — header.nunjucks — footer.nunjucks — build/ — css/ — inline.css — embedded.css — index.html — gulpfile.js — package.json </code></pre> <p>The <code>src/</code> directory contains all the working files. The templates are contained in the <code>templates/</code> directory, as well as any partials. The two main SASS files, <code>inline.scss</code> and <code>embedded.scss</code>are contained in the <code>sass/</code> directory. Any global data is held within the <code>data/</code> directory. By default, there is one file, <code>mailchimp.json</code> which contains global information about the mailchimp campaign. Finally, the <code>emails/</code> directory holds the actual emails being created, based off the templates.</p> <p>The <code>build/</code> directory contains the final output. This includes the CSS (both the files that are inlined or embedded) as well as the raw HTML.</p> <h2 id="setting-up-templating-partials-and-custom-data" tabindex="-1">Setting up Templating, Partials, and Custom Data <a class="header-anchor" href="https://bitsofco.de/a-gulp-workflow-for-building-html-email/">#</a></h2> <p>To enable templating and use of partials, I used nunjucks. Nunjucks is a really great templating language developed by Mozilla for JavaScript. To pass global data to the template files, I use the <code>gulp-data</code> plugin.</p> <p>Here is the <code>nunjucks</code> gulp task -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> nunjucksRender <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp-nunjucks-render'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">var</span> data <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp-data'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token keyword">var</span> globalData <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">mailchimp</span><span class="token operator">:</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'./src/data/mailchimp.json'</span><span class="token punctuation">)</span><br><span class="token punctuation">}</span><span class="token punctuation">;</span><br><br>gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'nunjucks'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">'sassEmbedded'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> gulp<span class="token punctuation">.</span><span class="token function">src</span><span class="token punctuation">(</span><span class="token string">'src/emails/*.nunjucks'</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span><br> <span class="token function">data</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> globalData<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 function">on</span><span class="token punctuation">(</span><span class="token string">'error'</span><span class="token punctuation">,</span> gutil<span class="token punctuation">.</span>log<span class="token punctuation">)</span><br> <span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span><br> <span class="token function">nunjucksRender</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">path</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'src/templates/'</span><span class="token punctuation">,</span> <span class="token string">'build/css/'</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 function">on</span><span class="token punctuation">(</span><span class="token string">'error'</span><span class="token punctuation">,</span> gutil<span class="token punctuation">.</span>log<span class="token punctuation">)</span><br> <span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span>gulp<span class="token punctuation">.</span><span class="token function">dest</span><span class="token punctuation">(</span><span class="token string">'build/'</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>This task does a few things -</p> <ul> <li>Wait for the <code>sassEmbedded</code> gulp task to be completed first. This is because, before we include the embedded.css file in the template, we want to make sure that it is the compiled CSS first</li> <li>Looks at the email files in <code>src/emails/*.nunjucks</code></li> <li>Passes those files the mailchmp data from <code>/src/data/mailchimp.json</code></li> <li>Generates the HTML page based on the templates and partials in the <code>src/templates/</code> and <code>build/css/</code> directories</li> <li>Builds the completed files into the <code>build/</code> directory</li> </ul> <h2 id="sass-compilation-with-post-css" tabindex="-1">SASS Compilation (with Post CSS) <a class="header-anchor" href="https://bitsofco.de/a-gulp-workflow-for-building-html-email/">#</a></h2> <p>I use <a href="https://ashleynolan.co.uk/blog/extend-sass-with-postcss">Ashley Nolan’s method</a> of combining SASS with PostCSS. This allows us to make use of PostCSS processors, while still writing SASS.</p> <p>Here are the two gulp tasks for the inline SASS and the embedded SASS -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> sass <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp-sass'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">var</span> postcss <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp-postcss'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">var</span> scss <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'postcss-scss'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">var</span> autoprefixer <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'autoprefixer'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token keyword">var</span> postcssProcessors <span class="token operator">=</span> <span class="token punctuation">[</span><br> <span class="token function">autoprefixer</span><span class="token punctuation">(</span> <span class="token punctuation">{</span> <span class="token literal-property property">browsers</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">'last 2 versions'</span><span class="token punctuation">,</span> <span class="token string">'ie > 10'</span><span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span><br><span class="token punctuation">]</span><br><br>gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'sassInline'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">callback</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> gulp<span class="token punctuation">.</span><span class="token function">src</span><span class="token punctuation">(</span><span class="token string">'src/sass/inline.scss'</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span><br> <span class="token function">postcss</span><span class="token punctuation">(</span>postcssProcessors<span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token literal-property property">syntax</span><span class="token operator">:</span> scss<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 function">pipe</span><span class="token punctuation">(</span><br> <span class="token function">sass</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token literal-property property">outputStyle</span><span class="token operator">:</span> <span class="token string">'expanded'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><br> <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> gutil<span class="token punctuation">.</span>log<span class="token punctuation">)</span><br> <span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span>gulp<span class="token punctuation">.</span><span class="token function">dest</span><span class="token punctuation">(</span><span class="token string">'build/css/'</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>gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'sassEmbedded'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">callback</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Similar to sassInline task</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>What it does -</p> <ul> <li>Applies PostCSS processors to the SCSS files</li> <li>Compiles the SCSS files to CSS</li> <li>Sends the complied CSS files to the <code>build/css/</code> directory</li> </ul> <h2 id="inlining-css" tabindex="-1">Inlining CSS <a class="header-anchor" href="https://bitsofco.de/a-gulp-workflow-for-building-html-email/">#</a></h2> <p>To inline the CSS, I use <code>gulp-inline-css</code>. This takes an HTML document, looks at CSS being applied to the page, and inlines it to the relevant elements.</p> <p>Here is the gulp task for inlining CSS -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> inlineCss <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp-inline-css'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'inlinecss'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">'sassInline'</span><span class="token punctuation">,</span> <span class="token string">'nunjucks'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> gulp<span class="token punctuation">.</span><span class="token function">src</span><span class="token punctuation">(</span><span class="token string">'build/*.html'</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span><br> <span class="token function">inlineCss</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">applyStyleTags</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span><br> <span class="token literal-property property">removeStyleTags</span><span class="token operator">:</span> <span class="token boolean">false</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br> <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> gutil<span class="token punctuation">.</span>log<span class="token punctuation">)</span><br> <span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span>gulp<span class="token punctuation">.</span><span class="token function">dest</span><span class="token punctuation">(</span><span class="token string">'build/'</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>What it does -</p> <ul> <li>Waits for the <code>nunjucks</code> and <code>sassInline</code> gulp tasks to be completed first</li> <li>Looks at the compiled HTML in the <code>build/</code> directory</li> <li>Inlines CSS from the <code>&lt;link rel=“stylesheet”&gt;</code> tag only, so the embedded CSS within the <code>&lt;style&gt;</code> element remains</li> <li>Replaces the file in the <code>build/</code> directory with the inlined CSS file</li> </ul> <h2 id="testing-with-real-data" tabindex="-1">Testing with Real Data <a class="header-anchor" href="https://bitsofco.de/a-gulp-workflow-for-building-html-email/">#</a></h2> <p>Now we have set up the various gulp tasks, we can begin writing emails. First, we need to create a template. The default template included in this workflow is <code>mailchimp.nunjucks</code>, which is catered to regular non-RSS MailChimp campaigns.</p> <pre class="language-hbs" tabindex="0"><code class="language-hbs"><span class="token punctuation">{</span><span class="token punctuation">%</span> <span class="token variable">include</span> <span class="token string">"partials/header.nunjucks"</span> <span class="token punctuation">%</span><span class="token punctuation">}</span><br><span class="token punctuation">&lt;</span><span class="token variable">center</span> <span class="token variable">id</span><span class="token punctuation">=</span><span class="token string">"body-container"</span><span class="token punctuation">></span><br><br> <span class="token punctuation">&lt;</span><span class="token punctuation">!</span><span class="token variable">--</span> <span class="token variable">PREHEADER</span> <span class="token variable">--</span><span class="token punctuation">></span><br> <span class="token punctuation">&lt;</span><span class="token variable">table</span> <span class="token variable">border</span><span class="token punctuation">=</span><span class="token string">"0"</span> <span class="token variable">cellpadding</span><span class="token punctuation">=</span><span class="token string">"0"</span> <span class="token variable">cellspacing</span><span class="token punctuation">=</span><span class="token string">"0"</span> <span class="token variable">id</span><span class="token punctuation">=</span><span class="token string">"template-preheader"</span> <span class="token variable">width</span><span class="token punctuation">=</span><span class="token string">"100%"</span><span class="token punctuation">></span><br> <span class="token punctuation">&lt;</span><span class="token variable">tr</span><span class="token punctuation">></span><br> <span class="token punctuation">&lt;</span><span class="token variable">td</span> <span class="token variable">mc</span><span class="token punctuation">:</span><span class="token variable">edit</span><span class="token punctuation">=</span><span class="token string">"preheader_left"</span> <span class="token variable">valign</span><span class="token punctuation">=</span><span class="token string">"top"</span> <span class="token variable">width</span><span class="token punctuation">=</span><span class="token string">"50%"</span><span class="token punctuation">></span><br> <span class="token punctuation">{</span><span class="token punctuation">%</span> <span class="token variable">block</span> <span class="token variable">preheader</span> <span class="token punctuation">%</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token punctuation">%</span> <span class="token variable">endblock</span> <span class="token punctuation">%</span><span class="token punctuation">}</span><br> <span class="token punctuation">&lt;</span><span class="token punctuation">/</span><span class="token variable">td</span><span class="token punctuation">></span><br> <span class="token punctuation">&lt;</span><span class="token variable">td</span> <span class="token variable">mc</span><span class="token punctuation">:</span><span class="token variable">edit</span><span class="token punctuation">=</span><span class="token string">"preheader_right"</span> <span class="token variable">valign</span><span class="token punctuation">=</span><span class="token string">"top"</span> <span class="token variable">width</span><span class="token punctuation">=</span><span class="token string">"50%"</span><span class="token punctuation">></span><br> <span class="token punctuation">&lt;</span><span class="token variable">a</span> <span class="token variable">href</span><span class="token punctuation">=</span><span class="token string">"*|ARCHIVE|*"</span><span class="token punctuation">></span><span class="token variable">View</span> <span class="token variable">this</span> <span class="token variable">email</span> <span class="token variable">in</span> <span class="token variable">your</span> <span class="token variable">browser</span><span class="token punctuation">&lt;</span><span class="token punctuation">/</span><span class="token variable">a</span><span class="token punctuation">></span><br> <span class="token punctuation">&lt;</span><span class="token punctuation">/</span><span class="token variable">td</span><span class="token punctuation">></span><br> <span class="token punctuation">&lt;</span><span class="token punctuation">/</span><span class="token variable">tr</span><span class="token punctuation">></span><br> <span class="token punctuation">&lt;</span><span class="token punctuation">/</span><span class="token variable">table</span><span class="token punctuation">></span><br><br> <span class="token punctuation">&lt;</span><span class="token punctuation">!</span><span class="token variable">--</span> <span class="token variable">The</span> <span class="token variable">rest</span> <span class="token variable">of</span> <span class="token variable">the</span> <span class="token variable">template</span> <span class="token variable">here</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token variable">--</span><span class="token punctuation">></span><br><br><span class="token punctuation">&lt;</span><span class="token punctuation">/</span><span class="token variable">center</span><span class="token punctuation">></span><br><span class="token punctuation">{</span><span class="token punctuation">%</span> <span class="token variable">include</span> <span class="token string">"partials/footer.nunjucks"</span> <span class="token punctuation">%</span><span class="token punctuation">}</span></code></pre> <p>To create an email based off of this template, we create a file in the <code>emails/</code> directory. We use the <code>{% extends %}</code> syntax to specify which template to use, and then we can then write the content for any blocks included in the template.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token comment">&lt;!-- Use mailchimp.nunjucks template --></span><br>{% extends "mailchimp.nunjucks" %}<br><br><span class="token comment">&lt;!-- Apply content to the preheader block --></span><br>{% block preheader %}<br>Lorem ipsum dolor sit amet<br>{% endblock %}</code></pre> <p>And that's it! You can get the files for the workflow from <a href="https://github.com/ireade/gulp-email-workflow">github</a> and start creating your own HTML emails hopefully a bit more pain-free. It's still a work in progress, so if you have any questions or suggestions, leave a comment below.</p> The Problem with iOS Safari and shrink-to-fit 2016-04-05T00:00:00Z https://bitsofco.de/ios-safari-and-shrink-to-fit/ <p>For a long time, the standard viewport meta tag went 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">&lt;</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>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>However, since iOS Safari 9.0 was introduced in September last year, a new additional value is now needed for most responsive websites, making this the new standard meta tag -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1, shrink-to-fit=no<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <h2 id="why-do-we-need-to-add-this-new-value" tabindex="-1">Why do we need to add this new value? <a class="header-anchor" href="https://bitsofco.de/ios-safari-and-shrink-to-fit/">#</a></h2> <p>To understand why we need to include this new value, we need to observe what happens if we don’t include it.</p> <p>Nowadays, the majority of elements are styled with responsive dimensions. This means using adaptive units like percentages and viewport units, rather than simple pixels. However, there are some circumstances in which a fixed-width element is included. And, the width of the element is not necessarily smaller than the width of the viewport. For example, a 800px-wide div on an iPhone.</p> <p>Normally, the behaviour we would expect is that there will be an overflow, and a scrollbar will be introduced to enable the user to see the rest of the width of the content. However, when Safari is in the new <strong>Split View mode</strong> (i.e. when there are multiple apps open side-by-side on the screen), this doesn't happen.</p> <p>What happens instead is that Safari manipulates the viewport to be equal to the width of the overflowing element, e.g. 800px, so that it's entirety fits on the screen. The effect of this is that the rest of the page is &quot;shrunk to fit&quot; the larger element.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/NKeylnIOeA-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/NKeylnIOeA-780.webp 780w"><img alt="Demo without shrink-to-fit viewport value" loading="lazy" decoding="async" src="https://bitsofco.de/img/NKeylnIOeA-780.png" width="780" height="400"></picture></p> <p>And we can also see that when the fixed-width element is not larger than the viewport, everything is normal.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/ZdPOCrLWQA-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/ZdPOCrLWQA-780.webp 780w"><img alt="Demo without shrink-to-fit viewport value" loading="lazy" decoding="async" src="https://bitsofco.de/img/ZdPOCrLWQA-780.png" width="780" height="400"></picture></p> <h2 id="why-does-safari-behave-this-way" tabindex="-1">Why does Safari behave this way? <a class="header-anchor" href="https://bitsofco.de/ios-safari-and-shrink-to-fit/">#</a></h2> <p>According to the developers working on WebKit, the reason for this is because, based on their data, the majority of websites were misusing the meta viewport tag.</p> <blockquote> <p><a href="https://twitter.com/KMuncie">@KMuncie</a> It is intentional. Only a minority of websites were using &quot;initial-scale=1&quot; or &quot;width=device-width&quot; correctly so this was added :( — Benjamin Poulain (@awfulben) <a href="https://twitter.com/awfulben/status/646382994496729088">September 22, 2015</a></p> </blockquote> <p>Instead of using the standard value of <code>width=device-width, initial-scale=1</code>, they were either hard-coding viewport sizes or missing it altogether. Therefore, because of the introduction of Split View, a lot of websites were not prepared for the enforced responsiveness and broke.</p> <h2 id="adding-shrink-to-fit" tabindex="-1">Adding <code>shrink-to-fit</code> <a class="header-anchor" href="https://bitsofco.de/ios-safari-and-shrink-to-fit/">#</a></h2> <p>Apple's response to this problem was to, by default, shrink all overflowing content on the page to fit the width of the browser viewport. For websites that are responsibly responsive, we can add the new viewport meta value, <code>shrink-to-fit=no</code>, to signal this to Safari and disable this default feature.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1, shrink-to-fit=no<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/BXE1WenbUB-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/BXE1WenbUB-780.webp 780w"><img alt="Demo with shrink-to-fit viewport value" loading="lazy" decoding="async" src="https://bitsofco.de/img/BXE1WenbUB-780.png" width="780" height="400"></picture></p> <p>Although we don't know if Apple will keep this around forever, it is certainly here to stay for the foreseeable future, until the balance between properly responsive websites and those that aren't falls on the correct side.</p> The Holy Grail Layout with CSS Grid 2016-03-29T00:00:00Z https://bitsofco.de/holy-grail-layout-css-grid/ <p>The <a href="https://drafts.csswg.org/css-grid/">CSS Grid Layout Module</a>, although still in Editor's Draft, is nearing finalisation. We can now <a href="https://igalia.github.io/css-grid-layout/enable.html">enable it in a number of browsers</a> for testing and help figure out any bugs it may have.</p> <p>The CSS Grid Layout is <em>really</em> complex, even more so than Flexbox. It has 17 new properties and introduces a lot of new concepts around the way we write css. So, in an attempt to wrap my head around this new specification and figure out how it works, I used it to create the Holy Grail Layout.</p> <h2 id="what-s-the-holy-grail-layout" tabindex="-1">What's the Holy Grail Layout? <a class="header-anchor" href="https://bitsofco.de/holy-grail-layout-css-grid/">#</a></h2> <p>The <a href="https://en.wikipedia.org/wiki/Holy_Grail_(web_design)">Holy Grail Layout</a> is a web page layout that consists of four sections - a header, footer, and a main content area with two sidebars, one on each side. The layout also adheres to the following rules -</p> <ul> <li>Has a <strong>fluid center column</strong> width <strong>fixed-width sidebars</strong></li> <li>The center column should appear <strong>first in the markup</strong>, before the two sidebars (but after the header)</li> <li>All three columns should be the <strong>same height</strong>, regardless of the content within them</li> <li>The footer should always be at the <strong>bottom of the browser viewport</strong>, even when the content doesn't fill up the height of the viewport</li> <li>The layout should be <strong>responsive</strong>, all the sections should collapse into one column on smaller viewports</li> </ul> <p>It is famously difficult to create simply in CSS without any hacks.</p> <h2 id="the-solution-with-css-grid" tabindex="-1">The Solution with CSS Grid <a class="header-anchor" href="https://bitsofco.de/holy-grail-layout-css-grid/">#</a></h2> <p>Here is the solution I came up with using the CSS Grid Layout. First, 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">&lt;</span>body</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>hg<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">&lt;</span>header</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>hg__header<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</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>hg__main<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Content<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>aside</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>hg__left<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Menu<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>aside</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>aside</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>hg__right<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Ads<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>aside</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>footer</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>hg__footer<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Footer<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>footer</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span></code></pre> <p>And the CSS, at only 31 lines (expanded!) -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.hg__header</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> header<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.hg__footer</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> footer<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.hg__main</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> main<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.hg__left</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> navigation<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.hg__right</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> ads<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><br><span class="token selector">.hg</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-areas</span><span class="token punctuation">:</span> <span class="token string">"header header header"</span><br> <span class="token string">"navigation main ads"</span><br> <span class="token string">"footer footer footer"</span><span class="token punctuation">;</span><br> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> 150px 1fr 150px<span class="token punctuation">;</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 100px<br> 1fr<br> 30px<span class="token punctuation">;</span><br> <span class="token property">min-height</span><span class="token punctuation">:</span> 100vh<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 600px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.hg</span> <span class="token punctuation">{</span><br> <span class="token property">grid-template-areas</span><span class="token punctuation">:</span> <span class="token string">"header"</span><br> <span class="token string">"navigation"</span><br> <span class="token string">"main"</span><br> <span class="token string">"ads"</span><br> <span class="token string">"footer"</span><span class="token punctuation">;</span><br> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 100px<br> 50px<br> 1fr<br> 50px<br> 30px<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/kNLXBGDfZK-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/kNLXBGDfZK-720.gif 720w"><img alt="Holy Grail Layout Responsive Demo" loading="lazy" decoding="async" src="https://bitsofco.de/img/kNLXBGDfZK-720.webp" width="720" height="446"></picture></p> <h2 id="breaking-it-down" tabindex="-1">Breaking it Down <a class="header-anchor" href="https://bitsofco.de/holy-grail-layout-css-grid/">#</a></h2> <p>As I mentioned, CSS Grid Layout can be very complicated. However, for the purpose of creating this layout, I only used 4 of the 17 new properties -</p> <ul> <li><code>grid-area</code></li> <li><code>grid-template-areas</code></li> <li><code>grid-template-columns</code></li> <li><code>grid-template-rows</code></li> </ul> <p>My solution to the Holy Grail Layout using these CSS Grid properties can be broken down into five steps.</p> <h3 id="1-defining-the-grid" tabindex="-1">1. Defining the Grid <a class="header-anchor" href="https://bitsofco.de/holy-grail-layout-css-grid/">#</a></h3> <p>The first thing we want to do is define the grid areas, so we can refer to them by this alias when creating the grid. We do this with the <code>grid-area</code> property.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.hg__header</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> header<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.hg__footer</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> footer<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.hg__main</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> main<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.hg__left</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> navigation<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.hg__right</span> <span class="token punctuation">{</span> <span class="token property">grid-area</span><span class="token punctuation">:</span> ads<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p>Then, using the <code>grid-template-areas</code> property, we can specify the layout of the grid in a really intuitive and visual way. The <code>grid-template-areas</code> property accepts a space-separated list of strings. Each string represents a row. Within each string, we have a space-separated list of grid areas. Each grid area defined takes up one column. So if we want an area to span two columns, we define it twice.</p> <p>In our Holy Grail Layout, we have 3 columns and 3 rows. The header and footer rows span 3 columns, while the other areas span 1 column each.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.hg</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-areas</span><span class="token punctuation">:</span> <span class="token string">"header header header"</span><br> <span class="token string">"navigation main ads"</span><br> <span class="token string">"footer footer footer"</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>With this markup, we get the following result.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/RltidRahif-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/RltidRahif-780.webp 780w"><img alt="Holy Grail Layout Step 1" loading="lazy" decoding="async" src="https://bitsofco.de/img/RltidRahif-780.png" width="780" height="512"></picture></p> <h3 id="2-defining-column-widths" tabindex="-1">2. Defining Column Widths <a class="header-anchor" href="https://bitsofco.de/holy-grail-layout-css-grid/">#</a></h3> <p>Next, we want to define the widths of the columns. We define the width of columns in our grid with the <code>grid-template-columns</code> property. This property accepts a space-separated list of widths, one for each column in the grid. Because we have 3 columns in our layout, we can specify 3 widths -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token property">grid-template-columns</span><span class="token punctuation">:</span> [column 1 width] [column 2 width] [column 3 width]<span class="token punctuation">;</span></code></pre> <p>For the Holy Grail Layout, we want the 2 sidebars to be 150px width each.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.hg</span> <span class="token punctuation">{</span><br> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> 150px [column 2 width] 150px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>We also want the middle column to take up the rest of the space. We can do this by using the new <code>fr</code> unit. This unit represents a fraction of the free space left in the grid. In our case, this adds up to the current width of the grid minus 300px (the width of the two sidebars).</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.hg</span> <span class="token punctuation">{</span><br> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> 150px 1fr 150px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>After setting the grid columns, this is what the layout looks like -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/SdfwF11Cz8-1916.avif 1916w"><source type="image/webp" srcset="https://bitsofco.de/img/SdfwF11Cz8-1916.webp 1916w"><img alt="Holy Grail Layout Step 2" loading="lazy" decoding="async" src="https://bitsofco.de/img/SdfwF11Cz8-1916.png" width="1916" height="1260"></picture></p> <h3 id="3-defining-row-heights" tabindex="-1">3. Defining Row Heights <a class="header-anchor" href="https://bitsofco.de/holy-grail-layout-css-grid/">#</a></h3> <p>Next, we want to define the heights of the rows. Similar to how we define the column widths with <code>grid-template-columns</code>, we define the row heights with <code>grid-template-rows</code>. This property also accepts a space-separated list of heights for each row in our grid. Although we can write it on one line, I think it's nicer and more visually clear to write it one row per line.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.hg</span> <span class="token punctuation">{</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 100px<br> 1fr<br> 30px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>So our header height is 100px, our footer height is 30px, and the middle row (with the main content and two sidebars) takes up the rest of the available space in the <code>.hg</code> element.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/gYojkyntKt-1918.avif 1918w"><source type="image/webp" srcset="https://bitsofco.de/img/gYojkyntKt-1918.webp 1918w"><img alt="Holy Grail Layout Step 3" loading="lazy" decoding="async" src="https://bitsofco.de/img/gYojkyntKt-1918.png" width="1918" height="1260"></picture></p> <h3 id="4-sticky-footer" tabindex="-1">4. Sticky Footer <a class="header-anchor" href="https://bitsofco.de/holy-grail-layout-css-grid/">#</a></h3> <p>In the Holy Grail Layout, we want the footer to always be at the bottom of the viewport, even if the content on the page is sparse. To achieve this, we can set a minimum height on the <code>.hg</code> element to be the height of the viewport.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.hg</span> <span class="token punctuation">{</span><br> <span class="token property">min-height</span><span class="token punctuation">:</span> 100vh<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Because we specified that the middle row should fill up the rest of the available space, it stretches to fill the screen.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/iUF7Qesl2f-1922.avif 1922w"><source type="image/webp" srcset="https://bitsofco.de/img/iUF7Qesl2f-1922.webp 1922w"><img alt="Holy Grail Layout Step 4" loading="lazy" decoding="async" src="https://bitsofco.de/img/iUF7Qesl2f-1922.png" width="1922" height="1266"></picture></p> <h3 id="5-making-it-responsive" tabindex="-1">5. Making it Responsive <a class="header-anchor" href="https://bitsofco.de/holy-grail-layout-css-grid/">#</a></h3> <p>Finally, we want to make the layout responsive. On smaller devices, all the grid items should be displayed in a single column, one after the other. To do this, we need to redefine the 3 properties we defined before - <code>grid-template-areas</code>, <code>grid-template-columns</code> and <code>grid-template-rows</code>.</p> <p>First, we want all the items in the grid to be in one column, in a particular order -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 600px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.hg</span> <span class="token punctuation">{</span><br> <span class="token property">grid-template-areas</span><span class="token punctuation">:</span> <span class="token string">"header"</span><br> <span class="token string">"navigation"</span><br> <span class="token string">"main"</span><br> <span class="token string">"ads"</span><br> <span class="token string">"footer"</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p>Next, we want all items to span the full width of the grid -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 600px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.hg</span> <span class="token punctuation">{</span><br> <span class="token property">grid-template-columns</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p>Finally, we need to reset the heights of each of the rows. All rows, besides the <code>main</code> row, have a defined height -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 600px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.hg</span> <span class="token punctuation">{</span><br> <span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 100px <span class="token comment">/* Header */</span><br> 50px <span class="token comment">/* Navigation */</span><br> 1fr <span class="token comment">/* Main Content */</span><br> 50px <span class="token comment">/* Ads */</span><br> 30px<span class="token punctuation">;</span> <span class="token comment">/* Footer */</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/hQqfjf9GTq-1022.avif 1022w"><source type="image/webp" srcset="https://bitsofco.de/img/hQqfjf9GTq-1022.webp 1022w"><img alt="Holy Grail Layout Step 5" loading="lazy" decoding="async" src="https://bitsofco.de/img/hQqfjf9GTq-1022.png" width="1022" height="1264"></picture></p> <p>And that's it! You can check out a <a href="https://ireade.github.io/holy-grail-css-grid/">demo of this here</a>, as well as the <a href="https://github.com/ireade/holy-grail-css-grid">source</a> (nb: you may need to enable experimental web features in your browser to see it).</p> <h2 id="support" tabindex="-1">Support <a class="header-anchor" href="https://bitsofco.de/holy-grail-layout-css-grid/">#</a></h2> <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> Better Detection and Analytics for Opera Browsers 2016-03-22T00:00:00Z https://bitsofco.de/better-detection-and-analytics-for-opera-browsers/ <p>In my article, <a href="https://bitsofco.de/wtf-opera-mini">WTF Opera Mini?!</a>, I wrote about the lack of support that the Opera Mini browser has for what I considered basic development features (e.g., font properties and 2d transforms).</p> <p>If you didn't already know, in the Opera Mini browser, the user has the option between two modes - <strong>High/Normal Savings Mode</strong> and <strong>Extreme Savings Mode</strong>. A lot of the complaints I had with Opera Mini are actually constrained to the Extreme Savings Mode in the browser. However, because there was no way to determine which mode was being used, I had to assume that all Opera Mini users were on the Extreme Savings Mode, especially as it is enabled by default.</p> <p>However, through a bit of research, I managed to find a way to detect which savings mode a user is currently on, as well some other useful information.</p> <h2 id="better-detection" tabindex="-1">Better Detection <a class="header-anchor" href="https://bitsofco.de/better-detection-and-analytics-for-opera-browsers/">#</a></h2> <p>The method I used to detect the various Opera Browsers and savings modes is through looking at the <code>navigator.userAgent</code> string returned by the browser. Here is an example user agent string returned from the Opera Mini Browser on a Nexus 5 running Android Marshmallow -</p> <pre><code>Mozilla/5.0 (Linux; U; Android 6.0.1; en-GB; Nexus 5 Build/MMB29V) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 OPR/15.0.2125.101257 Mobile Safari/537.36 </code></pre> <p>The user agent string differs across browsers, devices, and operating systems, but there are some commonalities between all Opera browsers we can use for detection. For example, we can detect if the browser is an Opera browser by checking for the following -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> isOpera <span class="token operator">=</span> window<span class="token punctuation">.</span>opera <span class="token operator">|</span><br> window<span class="token punctuation">.</span>opr <span class="token operator">|</span><br> navigator<span class="token punctuation">.</span>userAgent<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">' OPR/'</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token operator">-</span><span class="token number">1</span> <span class="token operator">|</span><br> navigator<span class="token punctuation">.</span>userAgent<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">' Coast/'</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token operator">-</span><span class="token number">1</span> <span class="token operator">|</span><br> navigator<span class="token punctuation">.</span>userAgent<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">' OPiOS/'</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">;</span></code></pre> <h3 id="detecting-savings-mode" tabindex="-1">Detecting Savings Mode <a class="header-anchor" href="https://bitsofco.de/better-detection-and-analytics-for-opera-browsers/">#</a></h3> <p>When Opera Mini is in Extreme Savings Mode, a completely different engine is used and this is reflected in the user agent string. For example, this is the user agent string for Opera Mini on the Nexus 5 in High versus Extreme Savings Mode -</p> <pre><code>// High Savings Mode (Normal) Mozilla/5.0 (Linux; U; Android 6.0.1; en-GB; Nexus 5 Build/MMB29V) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 OPR/15.0.2125.101257 Mobile Safari/537.36 // Extreme Savings Mode Opera/9.80 (Android; Opera Mini/15.0.2125/37.8025; U; en) Presto/2.12.423 Version/12.16 </code></pre> <p>In Extreme Savings Mode, the Presto engine is being used. So, we can determine which savings mode the user is currently on based on this information.</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>userAgent<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">'Presto/'</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token operator">-</span><span class="token number">1</span> <span class="token operator">&amp;&amp;</span><br> navigator<span class="token punctuation">.</span>userAgent<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">'Opera Mini/'</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token operator">-</span><span class="token number">1</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Extreme Savings Mode</span><br><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br> <span class="token comment">// High/Normal Savings Mode</span><br><span class="token punctuation">}</span></code></pre> <h3 id="detecting-other-information" tabindex="-1">Detecting Other Information <a class="header-anchor" href="https://bitsofco.de/better-detection-and-analytics-for-opera-browsers/">#</a></h3> <p>The user agent string can also give us more information about the browser and device being used. So, instead of just detecting which savings mode is being used, I wrote a small detection script that will determine the following information -</p> <ul> <li><strong>Browser</strong> - Opera, Opera Mini, Opera Mobile, or Opera Coast</li> <li><strong>Operating System</strong> - iOS, OSX, Windows, or Android</li> <li><strong>Platform</strong> - Mobile/Tablet, Desktop, or TV</li> <li><strong>Mode</strong> - High/Normal Savings or Extreme Savings</li> </ul> <p>The <code>opera-detect.js</code> script returns an object, <code>operaDetect</code>, with the following information -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> operaDetect <span class="token operator">=</span> <span class="token punctuation">{</span><br> <span class="token literal-property property">isOpera</span><span class="token operator">:</span> <span class="token comment">// Boolean, if the current browser is an Opera browser</span><br> <span class="token literal-property property">isExtremeMode</span><span class="token operator">:</span> <span class="token comment">// Boolean, if is the Extreme Savings Mode is on</span><br> <span class="token literal-property property">results</span><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 comment">// String, which Savings Mode is being used, e.g. "High/Normal Savings"</span><br> <span class="token literal-property property">platform</span><span class="token operator">:</span> <span class="token comment">// Which platform, e.g. "Mobile/Tablet"</span><br> <span class="token literal-property property">browser</span><span class="token operator">:</span> <span class="token comment">// e.g. "Opera Coast</span><br> <span class="token constant">OS</span><span class="token operator">:</span> <span class="token comment">// e.g. "Android"</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p>With this information, we can easily detect information about the savings mode a user is on, and alter their experience if necessary.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">if</span> <span class="token punctuation">(</span> operaDetect<span class="token punctuation">.</span>isExtremeMode <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// do stuff</span><br><span class="token punctuation">}</span></code></pre> <p>You can get this script to use yourself at my github repo, <a href="https://github.com/ireade/operadetect">ireade/operadetect</a>.</p> <h2 id="better-analytics" tabindex="-1">Better Analytics <a class="header-anchor" href="https://bitsofco.de/better-detection-and-analytics-for-opera-browsers/">#</a></h2> <p>Google Analytics has some great browser detection. Not only can you detect which browsers your visitors are using, but you can get more information on the browser such as versions. However, the default analytics are limited in two ways -</p> <ol> <li>No information on Savings Mode</li> <li>Opera Coast and Opera Mini (Normal Mode) on iOS show up as Safari, not Opera. (I assume this is because they use iOS Webview)</li> </ol> <p>To solve this, we can set up <a href="https://support.google.com/analytics/answer/2709828">custom dimensions</a> with Google Analytics to track the four pieces of information we receive from the <code>opera-detect.js</code> script. Once the custom dimensions are set up through Google Analytics, we can easily pass the information -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">if</span> <span class="token punctuation">(</span> operaDetect<span class="token punctuation">.</span>isOpera <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">ga</span><span class="token punctuation">(</span><span class="token string">'set'</span><span class="token punctuation">,</span> <span class="token string">'dimension1'</span><span class="token punctuation">,</span> operaDetect<span class="token punctuation">.</span>results<span class="token punctuation">.</span>mode<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token function">ga</span><span class="token punctuation">(</span><span class="token string">'set'</span><span class="token punctuation">,</span> <span class="token string">'dimension2'</span><span class="token punctuation">,</span> operaDetect<span class="token punctuation">.</span>results<span class="token punctuation">.</span>platform<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token function">ga</span><span class="token punctuation">(</span><span class="token string">'set'</span><span class="token punctuation">,</span> <span class="token string">'dimension3'</span><span class="token punctuation">,</span> operaDetect<span class="token punctuation">.</span>results<span class="token punctuation">.</span>browser<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token function">ga</span><span class="token punctuation">(</span><span class="token string">'set'</span><span class="token punctuation">,</span> <span class="token string">'dimension4'</span><span class="token punctuation">,</span> operaDetect<span class="token punctuation">.</span>results<span class="token punctuation">.</span><span class="token constant">OS</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token comment">// Send pageview</span><br><span class="token function">ga</span><span class="token punctuation">(</span><span class="token string">'send'</span><span class="token punctuation">,</span> <span class="token string">'pageview'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>To make things even easier, we can setup a custom dashboard to easily view this information -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/4AKmLiggXa-1187.avif 1187w"><source type="image/webp" srcset="https://bitsofco.de/img/4AKmLiggXa-1187.webp 1187w"><img alt="Custom Google Analytics Dashboard for Opera Detect Information" loading="lazy" decoding="async" src="https://bitsofco.de/img/4AKmLiggXa-1187.png" width="1187" height="551"></picture></p> <p>If you want to set these analytics up for your site, you can follow <a href="https://github.com/ireade/operadetect#analytics">these instructions</a>.</p> On :not and Specificity 2016-03-15T00:00:00Z https://bitsofco.de/on-not-and-specificity/ <p>The negation <a href="https://bitsofco.de/pseudo-classes-pseudo-elements-and-colon-notation">pseudo-class</a>, <code>:not</code>, can be incredibly useful. It allows us to target elements based on what attributes they <em>don't</em> have, rather than what they do. This helps us avoid writing extra, increasingly specific, rules in an attempt to override previous ones.</p> <p>A common example of this is when we want to apply a style to all list items, expect the last one. For example -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token comment">/* Without :not */</span><br><span class="token selector">li</span> <span class="token punctuation">{</span> <span class="token property">border-right</span><span class="token punctuation">:</span> 1px solid #000<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">li:last-child</span> <span class="token punctuation">{</span> <span class="token property">border-right</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><br><span class="token comment">/* Using :not */</span><br><span class="token selector">li:not(:last-child)</span> <span class="token punctuation">{</span> <span class="token property">border-right</span><span class="token punctuation">:</span> 1px solid #000<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p>The <code>:not</code> pseudo-class is one I use a lot. However, in my past use, I would occasionally run into scenarios in which the <code>:not</code> declaration would override a positive one. For example -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">a:not(.ul)</span> <span class="token punctuation">{</span> <span class="token property">text-decoration</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">nav a</span> <span class="token punctuation">{</span> <span class="token property">text-decoration</span><span class="token punctuation">:</span> underline<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p>When I did this, I found that the <code>nav a</code> elements would still not have an underline. I was initially a bit baffled as to why this would happen, until I did more reserach into how the <code>:not</code> rule actually works, and it's effect on specificity.</p> <h2 id="specificity-101" tabindex="-1">Specificity 101 <a class="header-anchor" href="https://bitsofco.de/on-not-and-specificity/">#</a></h2> <p>Specificity in CSS can be complicated to grasp, so it's best to start with an example. Consider 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">&lt;</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>foo<span class="token punctuation">"</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>bar<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</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">p</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> green<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">#bar</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> blue<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">p.foo#bar</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> yellow<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p>In cases like this where there are multiple selectors targeting the same element, which selector's rules will prevail is determined by its <strong>specificity</strong>. The specificity of a selector is determined by 2 things -</p> <ol> <li><strong>What kinds of selectors are used</strong>. There are three kinds of selectors - <ol> <li>ID Selectors, e.g. <code>#bar</code></li> <li>Class Selectors (including Pseudo-Classes), e.g. <code>.foo</code> or <code>:last-child</code></li> <li>Type Selectors, e.g. <code>p</code></li> </ol> </li> <li><strong>The number of each kind of selector</strong>. For example if there are 2 IDs and 3 Classes.</li> </ol> <p>These two factors combined determine the level of specificity for a selector. Using the example above, this is how specificity is calculated for each selector -</p> <table> <thead> <tr> <th style="text-align:left">Selector</th> <th style="text-align:left">No of IDs</th> <th style="text-align:left">No of Classes</th> <th style="text-align:left">No of Types</th> <th style="text-align:left">Specificity</th> <th style="text-align:left">Winner</th> </tr> </thead> <tbody> <tr> <td style="text-align:left"><code>p</code></td> <td style="text-align:left">0</td> <td style="text-align:left">0</td> <td style="text-align:left">1</td> <td style="text-align:left">0-0-1</td> <td style="text-align:left"></td> </tr> <tr> <td style="text-align:left"><code>.foo</code></td> <td style="text-align:left">0</td> <td style="text-align:left">1</td> <td style="text-align:left">0</td> <td style="text-align:left">0-1-0</td> <td style="text-align:left"></td> </tr> <tr> <td style="text-align:left"><code>#bar</code></td> <td style="text-align:left">1</td> <td style="text-align:left">0</td> <td style="text-align:left">0</td> <td style="text-align:left">1-0-0</td> <td style="text-align:left"></td> </tr> <tr> <td style="text-align:left"><code>p.foo#bar</code></td> <td style="text-align:left">1</td> <td style="text-align:left">1</td> <td style="text-align:left">1</td> <td style="text-align:left">1-1-1</td> <td style="text-align:left">✓</td> </tr> <tr> <td style="text-align:left"> **The way to read the resulting specificity is not to read it as one number, but to consider each unit itself, from left to right**.</td> <td style="text-align:left"></td> <td style="text-align:left"></td> <td style="text-align:left"></td> <td style="text-align:left"></td> <td style="text-align:left"></td> </tr> </tbody> </table> <p>When comparing two selectors, we take the first value (representing the number of ID Selectors) and compare that. If one selector has a higher ID value, then it automatically wins the specificity battle. If, and only if, the two selectors are equal in that value, do we move on to the next value (representing the number of Class Selectors), and so on.</p> <p>Consider the following examples -</p> <table> <thead> <tr> <th style="text-align:left">Selector</th> <th style="text-align:left">No of IDs</th> <th style="text-align:left">No of Classes</th> <th style="text-align:left">No of Types</th> <th style="text-align:left">Specificity</th> <th style="text-align:left">Winner</th> </tr> </thead> <tbody> <tr> <td style="text-align:left"><code>p</code></td> <td style="text-align:left">0</td> <td style="text-align:left">0</td> <td style="text-align:left">1</td> <td style="text-align:left">0-0-1</td> <td style="text-align:left"></td> </tr> <tr> <td style="text-align:left"><code>p:last-child</code></td> <td style="text-align:left">0</td> <td style="text-align:left">1</td> <td style="text-align:left">1</td> <td style="text-align:left">0-1-1</td> <td style="text-align:left"></td> </tr> <tr> <td style="text-align:left"><code>p.foo.bar.baz</code></td> <td style="text-align:left">0</td> <td style="text-align:left">3</td> <td style="text-align:left">1</td> <td style="text-align:left">0-3-1</td> <td style="text-align:left"></td> </tr> <tr> <td style="text-align:left"><code>#bar</code></td> <td style="text-align:left">1</td> <td style="text-align:left">0</td> <td style="text-align:left">0</td> <td style="text-align:left">1-0-0</td> <td style="text-align:left">✓</td> </tr> <tr> <td style="text-align:left"> **A single ID will always beat 100 classes**, because the number of classes is irrelevant if IDs exist.</td> <td style="text-align:left"></td> <td style="text-align:left"></td> <td style="text-align:left"></td> <td style="text-align:left"></td> <td style="text-align:left"></td> </tr> </tbody> </table> <p>(As a side note, it is important to remember that these calculations only apply to styles defined in CSS. Inline styles override all selector-based styles.)</p> <h2 id="what-about-not" tabindex="-1">What about :not? <a class="header-anchor" href="https://bitsofco.de/on-not-and-specificity/">#</a></h2> <p>The <code>:not</code> itself doesn't add anything to the specificity number as other pseudo-classes do. However, <strong>the selectors within the <code>:not</code> do</strong>.</p> <blockquote> <p>With respect to specificity, adding <code>p:not(.foo)</code>, is essentially the same as adding <code>.notFoo</code> to all <code>p</code>'s that do not have the the class of <code>.foo</code></p> </blockquote> <p>Fo example -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">p.bar</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">p:not(.foo)</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> green<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p>What colour would you expect <code>&lt;p class=&quot;bar&quot;&gt;</code> to be? The correct answer is green, not red.</p> <p>This is because, by adding the the <code>:not</code> rule, we have essentially added a class <code>.notFoo</code> to our <code>&lt;p class=&quot;bar&quot;&gt;</code> element.</p> <table> <thead> <tr> <th style="text-align:left">Selector</th> <th style="text-align:left">No of IDs</th> <th style="text-align:left">No of Classes</th> <th style="text-align:left">No of Types</th> <th style="text-align:left">Specificity</th> <th style="text-align:left">Winner</th> </tr> </thead> <tbody> <tr> <td style="text-align:left"><code>p.bar</code></td> <td style="text-align:left">0</td> <td style="text-align:left">1</td> <td style="text-align:left">1</td> <td style="text-align:left">0-1-1</td> <td style="text-align:left">Equal</td> </tr> <tr> <td style="text-align:left"><code>p:not(.foo)</code></td> <td style="text-align:left">0</td> <td style="text-align:left">1</td> <td style="text-align:left">1</td> <td style="text-align:left">0-1-1</td> <td style="text-align:left">Equal</td> </tr> <tr> <td style="text-align:left"> The `:not` rule is now the same level of specificity as the positive class element. So, because it is defined later in the CSS, it prevails. This also explains why, in the `nav` example, the `:not` rule won over the nested type selectors.</td> <td style="text-align:left"></td> <td style="text-align:left"></td> <td style="text-align:left"></td> <td style="text-align:left"></td> <td style="text-align:left"></td> </tr> </tbody> </table> <table> <thead> <tr> <th style="text-align:left">Selector</th> <th style="text-align:left">No of IDs</th> <th style="text-align:left">No of Classes</th> <th style="text-align:left">No of Types</th> <th style="text-align:left">Specificity</th> <th style="text-align:left">Winner</th> </tr> </thead> <tbody> <tr> <td style="text-align:left"><code>a:not(.ul)</code></td> <td style="text-align:left">0</td> <td style="text-align:left">1</td> <td style="text-align:left">1</td> <td style="text-align:left">0-1-1</td> <td style="text-align:left">✓</td> </tr> <tr> <td style="text-align:left"><code>nav a</code></td> <td style="text-align:left">0</td> <td style="text-align:left">0</td> <td style="text-align:left">2</td> <td style="text-align:left">0-0-2</td> <td style="text-align:left"></td> </tr> </tbody> </table> <p><strong>Things get even more messy when we introduce IDs</strong>. The same way that <code>p:not(.foo)</code> is almost like adding <code>.notFoo</code> to all <code>&lt;p&gt;</code> elements, adding <code>p:not(#bar)</code> is like adding <code>#notBar</code> to all <code>&lt;p&gt;</code> elements. For example -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">p:not(#foo)</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> green<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">p.bar</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p>What colour would you expect <code>&lt;p class=&quot;bar&quot;&gt;</code> to be? The correct answer is green, not red.</p> <table> <thead> <tr> <th>Selector</th> <th>No of IDs</th> <th>No of Classes</th> <th>No of Types</th> <th>Specificity</th> <th>Winner</th> </tr> </thead> <tbody> <tr> <td><code>p.bar</code></td> <td>0</td> <td>1</td> <td>1</td> <td>0-1-1</td> <td></td> </tr> <tr> <td><code>p:not(#foo)</code></td> <td>1</td> <td>0</td> <td>1</td> <td>1-0-1</td> <td>✓</td> </tr> <tr> <td> Even though our positive class element is defined later in the css, we have essentially inadvertently added an ID to the element from the `:not` rule! 😱</td> <td></td> <td></td> <td></td> <td></td> <td></td> </tr> </tbody> </table> <h2 id="using-not" tabindex="-1">Using :not <a class="header-anchor" href="https://bitsofco.de/on-not-and-specificity/">#</a></h2> <p>This effect of <code>:not</code> has made me re-think the way I use it. Even though it feels like we are bypassing the need to write increasingly specific rules to override others, like in the <code>li:last-child</code> example, it seems like <code>:not</code> inadvertently does the same thing.</p> <p>I will definitely keep using <code>:not</code> because, in many circumstances, it is still the cleaner way to write styles. However, I will use it with a few caveats -</p> <ul> <li><strong>Never</strong> use it with IDs, e.g. <code>:not(#bar)</code></li> <li>Restrict using it with generic type selectors, e.g. <code>div:not(.foo)</code></li> <li>Define <code>:not</code> rules earlier in CSS so they can be overridden if necessary</li> </ul> Styling Broken Images 2016-03-08T00:00:00Z https://bitsofco.de/styling-broken-images/ <p>Broken images are ugly.</p> <img src="https://bitsofco.de/broken.jpg" alt="This image is broken! Ugly, isn’t it?"> <p>But they don’t always have to be. We can use CSS to apply styles to the <code>&lt;img&gt;</code> element to provide a better experience than the default.</p> <h2 id="two-facts-about-the-img-element" tabindex="-1">Two Facts About The <code>&lt;img&gt;</code> Element <a class="header-anchor" href="https://bitsofco.de/styling-broken-images/">#</a></h2> <p>To understand how we can style broken images, there are two facts about the way the <code>&lt;img&gt;</code> element behaves that we need to understand first.</p> <ol> <li> <p><strong>We can apply regular typography-related styling to the <code>&lt;img&gt;</code> element</strong>. These styles will be applied to the alternative text, if it is displayed, and will not affect the working image.</p> </li> <li> <p><strong>The <code>&lt;img&gt;</code> element is a <a href="https://www.w3.org/TR/CSS21/generate.html#before-after-content">replaced element</a></strong>. This is an element “whose appearance and dimensions are defined by an external resource” (<a href="https://reference.sitepoint.com/css/replacedelements">Sitepoint</a>). Because the element is controlled by an external source, the <code>:before</code> and <code>:after</code> pseudo-elements typically shouldn’t work with it. However, <strong>when the image is broken and not loaded, these pseudo-elements can appear</strong>.</p> </li> </ol> <p>Because of these two facts, we are able to apply styles to the <code>&lt;img&gt;</code> element that will only appear when the image is broken, and will leave a working image unaffected.</p> <h2 id="putting-it-into-practice" tabindex="-1">Putting It Into Practice <a class="header-anchor" href="https://bitsofco.de/styling-broken-images/">#</a></h2> <p>Using this information, here are some examples of how we can style broken images, using the following broken link -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>https://bitsofco.de/broken.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>Kanye Laughing<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <h3 id="adding-helpful-information" tabindex="-1">Adding Helpful Information <a class="header-anchor" href="https://bitsofco.de/styling-broken-images/">#</a></h3> <p>One way we can handle broken images is to provide a message to the user saying that the image is broken. Using the <code>attr()</code> expression, we can even display the link to the broken image.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/C106CMHeAK-889.avif 889w"><source type="image/webp" srcset="https://bitsofco.de/img/C106CMHeAK-889.webp 889w"><img alt="Using alt text to add helping information" loading="lazy" decoding="async" src="https://bitsofco.de/img/C106CMHeAK-889.png" width="889" height="196"></picture></p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">img</span> <span class="token punctuation">{</span><br> <span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">'Helvetica'</span><span class="token punctuation">;</span><br> <span class="token property">font-weight</span><span class="token punctuation">:</span> 300<span class="token punctuation">;</span><br> <span class="token property">line-height</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span><br> <span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span><br><br> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> auto<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 property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">img:before</span> <span class="token punctuation">{</span><br> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"We're sorry, the image below is broken :("</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 property">margin-bottom</span><span class="token punctuation">:</span> 10px<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">img:after</span> <span class="token punctuation">{</span><br> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"(url: "</span> <span class="token function">attr</span><span class="token punctuation">(</span>src<span class="token punctuation">)</span> <span class="token string">")"</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 property">font-size</span><span class="token punctuation">:</span> 12px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h3 id="replacing-the-default-alternative-text" tabindex="-1">Replacing The Default Alternative Text <a class="header-anchor" href="https://bitsofco.de/styling-broken-images/">#</a></h3> <p>Alternatively, we can use the pseudo-elements to replace the default alt text that shows, by positioning the pseudo-element on top of the default text, hiding it from view.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/TkHTANZbw2-890.avif 890w"><source type="image/webp" srcset="https://bitsofco.de/img/TkHTANZbw2-890.webp 890w"><img alt="Using alt text to replace the default alt text" loading="lazy" decoding="async" src="https://bitsofco.de/img/TkHTANZbw2-890.png" width="890" height="118"></picture></p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">img</span> <span class="token punctuation">{</span> <span class="token comment">/* Same as first example */</span> <span class="token punctuation">}</span><br><br><span class="token selector">img:after</span> <span class="token punctuation">{</span><br> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"\f1c5"</span> <span class="token string">" "</span> <span class="token function">attr</span><span class="token punctuation">(</span>alt<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 16px<span class="token punctuation">;</span><br> <span class="token property">font-family</span><span class="token punctuation">:</span> FontAwesome<span class="token punctuation">;</span><br> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>100<span class="token punctuation">,</span> 100<span class="token punctuation">,</span> 100<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token property">display</span><span class="token punctuation">:</span> block<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">z-index</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span><br> <span class="token property">top</span><span class="token punctuation">:</span> 0<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">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> #fff<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h3 id="extra-styling" tabindex="-1">Extra Styling <a class="header-anchor" href="https://bitsofco.de/styling-broken-images/">#</a></h3> <p>In addition to (or instead of) displaying a custom message, we can use the pseudo-elements to apply more styling to the broken image.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/mcBWyvELct-891.avif 891w"><source type="image/webp" srcset="https://bitsofco.de/img/mcBWyvELct-891.webp 891w"><img alt="Using alt text to apply styling to the broken image" loading="lazy" decoding="async" src="https://bitsofco.de/img/mcBWyvELct-891.png" width="891" height="164"></picture></p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">img</span> <span class="token punctuation">{</span><br> <span class="token comment">/* Same as first example */</span><br> <span class="token property">min-height</span><span class="token punctuation">:</span> 50px<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">img: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">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span><br><br> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span><br> <span class="token property">top</span><span class="token punctuation">:</span> -10px<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">height</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% + 10px<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>230<span class="token punctuation">,</span> 230<span class="token punctuation">,</span> 230<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">border</span><span class="token punctuation">:</span> 2px dotted <span class="token function">rgb</span><span class="token punctuation">(</span>200<span class="token punctuation">,</span> 200<span class="token punctuation">,</span> 200<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">border-radius</span><span class="token punctuation">:</span> 5px<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">img:after</span> <span class="token punctuation">{</span><br> <span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">"\f127"</span> <span class="token string">" Broken Image of "</span> <span class="token function">attr</span><span class="token punctuation">(</span>alt<span class="token punctuation">)</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 property">font-size</span><span class="token punctuation">:</span> 16px<span class="token punctuation">;</span><br> <span class="token property">font-style</span><span class="token punctuation">:</span> normal<span class="token punctuation">;</span><br> <span class="token property">font-family</span><span class="token punctuation">:</span> FontAwesome<span class="token punctuation">;</span><br> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>100<span class="token punctuation">,</span> 100<span class="token punctuation">,</span> 100<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span><br> <span class="token property">top</span><span class="token punctuation">:</span> 5px<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">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>If the image is not broken, with all the same styles applied to the element, the image is displayed normally. The pseudo-elements are not generated at all.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/soQUv8hfVD-872.avif 872w"><source type="image/webp" srcset="https://bitsofco.de/img/soQUv8hfVD-872.webp 872w"><img alt="Image not broken" loading="lazy" decoding="async" src="https://bitsofco.de/img/soQUv8hfVD-872.png" width="872" height="588"></picture></p> <h2 id="browser-compatibility" tabindex="-1">Browser Compatibility <a class="header-anchor" href="https://bitsofco.de/styling-broken-images/">#</a></h2> <p>Unfortunately, not all browsers handle broken images in the same way. For some browsers, even though the image is not displayed, the pseudo-elements don't show up at all.</p> <p>Here’s what I’ve found from my tests -</p> <table> <thead> <tr> <th style="text-align:left">Browser</th> <th style="text-align:left">Alt Text</th> <th style="text-align:left">:before</th> <th style="text-align:left">:after</th> </tr> </thead> <tbody> <tr> <td style="text-align:left">Chrome (Desktop and Android)</td> <td style="text-align:left">✓</td> <td style="text-align:left">✓</td> <td style="text-align:left">✓</td> </tr> <tr> <td style="text-align:left">Firefox (Desktop and Android)</td> <td style="text-align:left">✓</td> <td style="text-align:left">✓</td> <td style="text-align:left">✓</td> </tr> <tr> <td style="text-align:left">Opera (Desktop)</td> <td style="text-align:left">✓</td> <td style="text-align:left">✓</td> <td style="text-align:left">✓</td> </tr> <tr> <td style="text-align:left">Opera Mini</td> <td style="text-align:left">✓ **</td> <td style="text-align:left">✗</td> <td style="text-align:left">✗</td> </tr> <tr> <td style="text-align:left">Safari (Desktop and iOS)</td> <td style="text-align:left">✓ *</td> <td style="text-align:left">✗</td> <td style="text-align:left">✗</td> </tr> <tr> <td style="text-align:left">iOS Webview (Chrome, Firefox, others)</td> <td style="text-align:left">✓ *</td> <td style="text-align:left">✗</td> <td style="text-align:left">✗</td> </tr> </tbody> </table> <p>* The alt text will only show if the width of the image is large enough to accommodate it. If no width is specified for the img, the alt text may not be displayed at all ** Font styling not applied</p> <p>For browsers that don’t support the pseudo-elements, the styles applied are ignored, so they don’t interfere in a breaking way. This means that we can still apply the styles and serve a more pleasant experience for users on a supporting browser.</p> Challenge - Recreating Clear with Pure JavaScript 2016-03-01T00:00:00Z https://bitsofco.de/clear-js/ <p>If you don’t already follow <a href="https://twitter.com/wesbos">@wesbos</a> on twitter, you probably should. Recently, he has been posting some interesting javascript challenges - see the <a href="https://codepen.io/wesbos/pen/zrLjYq">first one</a> and the <a href="https://codepen.io/wesbos/pen/JGVryP">second one</a>. I found those challenges really helpful, so I decided to create one myself.</p> <p>I use the todo application, Clear, a lot and thought it would be an interesting challenge to recreate the core interactions in the browser.</p> <h2 id="the-challenge" tabindex="-1">The Challenge <a class="header-anchor" href="https://bitsofco.de/clear-js/">#</a></h2> <p>This is what the final product looks like -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/G8daGqz9b2-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/G8daGqz9b2-720.gif 720w"><img alt="Clear with JavaScript" loading="lazy" decoding="async" src="https://bitsofco.de/img/G8daGqz9b2-720.webp" width="720" height="456"></picture></p> <p><a href="https://codepen.io/ire/pen/MyWwJy">Start the Challenge</a> | <a href="https://codepen.io/ire/pen/NNPaEQ">Live Demo with My Solution</a></p> <p>The key interactions -</p> <ul> <li><strong>Drag a list item right to mark it as complete</strong> - Once the user has dragged the item past a certain point and releases the mouse, the item should swipe away fully.</li> <li><strong>Completed items appear at the bottom</strong> - The completed item should reappear at the bottom of the list.</li> <li><strong>Drag a list item left to delete it</strong> - Deleted items are removed from the HTML completely.</li> <li><strong>Drag left on a completed item to delete it</strong> - The user can drag a completed item left to delete it. Dragging right should not do anything.</li> </ul> <p>If you want to try out this challenge, you can get the <a href="https://codepen.io/ire/pen/MyWwJy">boilerplate HTML and CSS here</a>. The aim of the challenge is to use only JavaScript, but if you’re a beginner you can use jQuery as well.</p> <h2 id="my-solution" tabindex="-1">My Solution <a class="header-anchor" href="https://bitsofco.de/clear-js/">#</a></h2> <p>If you're going to try the challenge yourself, don't look past here. Otherwise, this is the solution that I came up with. It can be summarised in four key functions.</p> <h3 id="addmousedowneventlistener" tabindex="-1">addMouseDownEventListener() <a class="header-anchor" href="https://bitsofco.de/clear-js/">#</a></h3> <p>In this function, I add an event listener on <code>mousedown</code> for all of the list items. When this event is fired, 2 other event listeners are added - <code>mousemove</code> and <code>mouseup</code>.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">addMouseDownEventListener</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Reset values to use in handleMouseMove()</span><br> cursorXPosition <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><br> cursorXPositionDiff <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Loop through all tasks and add the event listener to each</span><br> <span class="token keyword">for</span> <span class="token punctuation">(</span> <span class="token keyword">var</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> tasks<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> tasks<span class="token punctuation">[</span>i<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">"mousedown"</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 keyword">this</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"mousemove"</span><span class="token punctuation">,</span> handleMouseMove<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"mouseup"</span><span class="token punctuation">,</span> handleMouseUp<span class="token punctuation">,</span> <span class="token boolean">false</span><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><br><span class="token punctuation">}</span></code></pre> <h3 id="handlemousemove" tabindex="-1">handleMouseMove() <a class="header-anchor" href="https://bitsofco.de/clear-js/">#</a></h3> <p>This is the function that is called when the user has clicked and moved the mouse together (so essentially, dragging). It does two things -</p> <ol> <li>Drags the list item along with the mouse</li> <li>Adds a class of <code>.completing</code> or <code>.deleting</code> depending on if the list item has been dragged right/left by a certain threshold</li> </ol> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> <span class="token function-variable function">handleMouseMove</span> <span class="token operator">=</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> <span class="token keyword">if</span> <span class="token punctuation">(</span> cursorXPosition <span class="token operator">===</span> <span class="token number">0</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> cursorXPosition <span class="token operator">=</span> event<span class="token punctuation">.</span>x<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><br> <span class="token comment">// Set the margin-left of the list item to move with the mouse</span><br> cursorXPositionDiff <span class="token operator">=</span> event<span class="token punctuation">.</span>x <span class="token operator">-</span> cursorXPosition<span class="token punctuation">;</span><br> event<span class="token punctuation">.</span>target<span class="token punctuation">.</span>style<span class="token punctuation">.</span>marginLeft <span class="token operator">=</span> cursorXPositionDiff <span class="token operator">+</span> <span class="token string">'px'</span><span class="token punctuation">;</span><br><br> <span class="token keyword">var</span> taskIsNotCompleted <span class="token operator">=</span> <span class="token operator">!</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>target<span class="token punctuation">.</span>className<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">"completed"</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Add class if the cursorXPositionDiff gets to a certain amount (40px)</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> cursorXPositionDiff <span class="token operator">></span> <span class="token number">40</span> <span class="token operator">&amp;&amp;</span> taskIsNotCompleted <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> event<span class="token punctuation">.</span>target<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">'completing'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> cursorXPositionDiff <span class="token operator">&lt;</span> <span class="token operator">-</span><span class="token number">40</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> event<span class="token punctuation">.</span>target<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">'deleting'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br> event<span class="token punctuation">.</span>target<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">'completing'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> event<span class="token punctuation">.</span>target<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">'deleting'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <h3 id="handlemouseup" tabindex="-1">handleMouseUp() <a class="header-anchor" href="https://bitsofco.de/clear-js/">#</a></h3> <p>This is the function that is called after the user releases the grip on the list item. It checks if the item has been marked as <code>.completing</code>, <code>.deleting</code>, or neither, and handles each case -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> <span class="token function-variable function">handleMouseUp</span> <span class="token operator">=</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> <span class="token keyword">var</span> className <span class="token operator">=</span> event<span class="token punctuation">.</span>target<span class="token punctuation">.</span>className<span class="token punctuation">;</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> className<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">'completing'</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token operator">-</span><span class="token number">1</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// SWIPE AWAY CURRENT LIST ITEM (RIGHT)</span><br> <span class="token function">swipeElement</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>target<span class="token punctuation">.</span>id<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> className<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">'deleting'</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token operator">-</span><span class="token number">1</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// SWIPE AWAY CURRENT LIST ITEM (LEFT)</span><br> <span class="token function">swipeElement</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>target<span class="token punctuation">.</span>id<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span><br> <span class="token comment">// REPOSITION LIST ITEM BACK TO NORMAL POSITION</span><br> event<span class="token punctuation">.</span>target<span class="token punctuation">.</span>style<span class="token punctuation">.</span>marginLeft <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token comment">// Remove mousemove listener, and re-add mousedown listeners to all list items</span><br> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">"mousemove"</span><span class="token punctuation">,</span> handleMouseMove<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token function">addMouseDownEventListener</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h3 id="swipeelement" tabindex="-1">swipeElement() <a class="header-anchor" href="https://bitsofco.de/clear-js/">#</a></h3> <p>This function handles the swiping away/in of the list items -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">function</span> <span class="token function">swipeElement</span><span class="token punctuation">(</span><span class="token parameter">elementID<span class="token punctuation">,</span> swipingRight<span class="token punctuation">,</span> swipingIn</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">var</span> element <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span>elementID<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Swiping an element out - marking as completed or done</span><br> <span class="token keyword">function</span> <span class="token function">swipeElementOut</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">var</span> currentMargin <span class="token operator">=</span> <span class="token function">getCurrentMargin</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">,</span><br> currentMargin <span class="token operator">=</span> swipingRight <span class="token operator">?</span> currentMargin <span class="token operator">+=</span> <span class="token number">1</span> <span class="token operator">:</span> currentMargin <span class="token operator">-=</span> <span class="token number">1</span><span class="token punctuation">;</span><br><br> element<span class="token punctuation">.</span>style<span class="token punctuation">.</span>marginLeft <span class="token operator">=</span> currentMargin <span class="token operator">+</span> <span class="token string">'%'</span><span class="token punctuation">;</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> currentMargin <span class="token operator">==</span> <span class="token number">100</span> <span class="token operator">|</span> currentMargin <span class="token operator">==</span> <span class="token operator">-</span><span class="token number">100</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">clearInterval</span><span class="token punctuation">(</span>interval<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token comment">// Either append completed item to bottom or delete item</span><br> swipingRight <span class="token operator">?</span> <span class="token function">appendCompletedItem</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span> <span class="token operator">:</span> element<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><br><br> <span class="token comment">// Swiping an element in - appending completed item to list</span><br> <span class="token keyword">function</span> <span class="token function">swipeElementIn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">var</span> currentMargin <span class="token operator">=</span> <span class="token function">getCurrentMargin</span><span class="token punctuation">(</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span><br> currentMargin <span class="token operator">-=</span> <span class="token number">1</span><span class="token punctuation">;</span><br><br> element<span class="token punctuation">.</span>style<span class="token punctuation">.</span>marginLeft <span class="token operator">=</span> currentMargin <span class="token operator">+</span> <span class="token string">'%'</span><span class="token punctuation">;</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> currentMargin <span class="token operator">==</span> <span class="token number">0</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">clearInterval</span><span class="token punctuation">(</span>interval<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 comment">// Choose which function to use</span><br> <span class="token keyword">var</span> interval <span class="token operator">=</span> swipingIn <span class="token operator">?</span> <span class="token function">setInterval</span><span class="token punctuation">(</span>swipeElementIn<span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token function">setInterval</span><span class="token punctuation">(</span>swipeElementOut<span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>There's still a lot I can do to improve this, but this is the solution I have found for now. You can view <a href="https://codepen.io/ire/pen/NNPaEQ">my full solution here</a>.</p> <p>The real value in doing these challenges is seeing how others tackle the problem. So, if you do this, do leave a comment with a link to your codepen/github and I’ll add a list of everyone’s solutions.</p> <h2 id="other-solutions" tabindex="-1">Other Solutions <a class="header-anchor" href="https://bitsofco.de/clear-js/">#</a></h2> <ul> <li><a href="https://codepen.io/Kallirroi/pen/EKVaRr">Kallirroi</a> (using jQuery)</li> <li><a href="https://codepen.io/thomasdigby/pen/QNNBNZ">thomasdigby</a></li> <li><a href="https://codepen.io/dlueth/pen/YqWVmq?editors=1010">dlueth</a></li> <li><a href="https://codepen.io/mustafaismail22/pen/KzGGpv/?editors=0010">mustafaismail22</a></li> <li><a href="https://codepen.io/Villike/pen/BKGbwY?editors=0010">Villike</a></li> <li><a href="https://codepen.io/caseykingsbury/pen/kXpxzq">caseykingsbury</a></li> </ul> 6 Reasons to Start Using Flexbox 2016-02-23T00:00:00Z https://bitsofco.de/6-reasons-to-start-using-flexbox/ <p><strong>Do you use flexbox</strong>? Last week on twitter, I asked this question and was surprised to find out that <a href="https://twitter.com/ireaderinokun/status/699577690450477057?ref_src=twsrc%5Etfw">less than 50% of people answered yes</a>. This was surprising to me because, up until recently, I wasn't using flexbox in any production projects either, and I thought I must be the only one.</p> <p>There were a number of reasons why I hadn't started using flexbox regularly but, having done more research, my concerns have subsided and I've become a convert. So, here are 6 reasons you should start using flexbox (too).</p> <h2 id="1-it-s-supported-in-all-major-browsers" tabindex="-1">1. It’s supported in all major browsers <a class="header-anchor" href="https://bitsofco.de/6-reasons-to-start-using-flexbox/">#</a></h2> <p>The main reason I hadn’t been using flexbox was because of a perceived lack of browser support. But in actual fact, flexbox is very well supported, at 95.89% global support. If you disregard IE 10 and below, <a href="https://www.microsoft.com/en-us/WindowsForBusiness/End-of-IE-support">which Microsoft says you now can</a>, this number is even higher.</p> <p class="ciu_embed" data-feature="flexbox" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false"> <picture> <source type="image/webp" srcset="https://caniuse.bitsofco.de/image/flexbox.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/flexbox.png"> <img src="https://caniuse.bitsofco.de/image/flexbox.jpg" alt="Data on support for the flexbox feature across the major browsers from caniuse.com"> </picture> </p> <p>Flexbox has even more support than other features I still use such as <a href="https://caniuse.com/#feat=transforms2d">2D transforms (with 91.85%)</a> or even <a href="https://caniuse.com/#search=position">position:fixed (with 92.98%)</a>.</p> <h2 id="2-you-don-t-have-to-worry-about-syntax" tabindex="-1">2. You don’t have to worry about syntax <a class="header-anchor" href="https://bitsofco.de/6-reasons-to-start-using-flexbox/">#</a></h2> <p>Even though the current syntax of flexbox is supported in the current versions of all browsers, what about the old versions? Because the syntax has changed over the years, there is some inconsistency in how to write it. To support the last 2 versions of all browsers currently, we would have to write at least 4 versions of every rule using various vendor prefixes.</p> <p>The solution to this I have settled on is to <strong>just use <a href="https://github.com/postcss/autoprefixer">autoprefixer</a></strong>. Keeping track of which vendor prefixes we need to use for what isn’t necessarily the best use of our time and effort, so we can and should automate it.</p> <p>Using autoprefixer, we can specify which browser versions we want to support, and the correct vendor prefixes will be automatically added.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token comment">/* Write this */</span><br><span class="token selector">.flex-container</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token comment">/* Compiles to this (with autoprefixer set to support last 2 versions of all browsers) */</span><br><span class="token selector">.flex-container</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> -webkit-box<span class="token punctuation">;</span><br> <span class="token property">display</span><span class="token punctuation">:</span> -webkit-flex<span class="token punctuation">;</span><br> <span class="token property">display</span><span class="token punctuation">:</span> -ms-flexbox<span class="token punctuation">;</span><br> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="3-it-can-be-simple-to-get-started" tabindex="-1">3. It can be simple to get started <a class="header-anchor" href="https://bitsofco.de/6-reasons-to-start-using-flexbox/">#</a></h2> <p>Admittedly, learning <em>all</em> of flexbox isn’t so straightforward. There are <a href="https://www.w3.org/TR/css-flexbox-1/#property-index">12 new properties</a>, each with an average of 4 potential values. It can be a bit overwhelming to jump right in with everything.</p> <p><strong>But you don’t have to start with everything</strong>. For a great number of circumstances, I find myself only needing to use 3 properties -</p> <ul> <li><code>display</code> - This sets the element as an inline or block flexbox container element</li> <li><code>justify-content</code> - This controls the horizontal alignment of items within the flex container (If the flex-direction is the default value of row or row-reverse)</li> <li><code>align-items</code> - This controls the vertical alignment of items within the flex container (If the flex-direction is the default value of row or row-reverse)</li> </ul> <p>With these properties alone, which are applied to the flex container, we can produce a lot of different layouts. And when you’re ready to learn more, there is an abundance of resources/cheatsheets/exercises to help you learn -</p> <ul> <li><a href="https://codepen.io/enxaneta/full/adLPwv/">Flexbox Playground</a> - A live document you can play around with to test the effect of each property-value pair</li> <li><a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/">A Complete Guide to Flexbox</a> (CSS Tricks) - An overview of all of flexbox</li> <li><a href="https://flexboxfroggy.com/">Flexbox Froggy</a> - A game for learning CSS flexbox</li> <li><a href="https://github.com/philipwalton/flexbugs">Flexbugs</a> - A community-curated list of flexbox issues and cross-browser workarounds for them</li> <li><a href="https://github.com/10up/flexibility">Flexibility</a> - A polyfill to support legacy browsers</li> </ul> <h2 id="4-you-can-finally-centre-elements" tabindex="-1">4. You can finally centre elements <a class="header-anchor" href="https://bitsofco.de/6-reasons-to-start-using-flexbox/">#</a></h2> <p>Besides the support for flexbox, a reason to use it is the ease in which we can centre elements, both horizontally and vertically.</p> <p>With only 3 declarations, we can achieve a perfectly centred child element -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.flex-container</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span><br> <span class="token property">justify-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span> <span class="token comment">/* horizontal centering */</span><br> <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span> <span class="token comment">/* vertical centering */</span><br><br> <span class="token property">border</span><span class="token punctuation">:</span> 2px dashed #000<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Wv2t1jL9sL-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/Wv2t1jL9sL-780.webp 780w"><img alt="div centred with flexbox" loading="lazy" decoding="async" src="https://bitsofco.de/img/Wv2t1jL9sL-780.png" width="780" height="414"></picture></p> <h2 id="5-you-can-manipulate-inline-elements-easily" tabindex="-1">5. You can manipulate inline elements easily <a class="header-anchor" href="https://bitsofco.de/6-reasons-to-start-using-flexbox/">#</a></h2> <p>One issue with having inline items positioned beside each other is the <a href="https://css-tricks.com/fighting-the-space-between-inline-block-elements/">infamous extra 4px margin</a>. Although there are ways to get around this, like floating the elements, that comes with its own problems as well.</p> <p>With flexbox, we can deal with inline elements effortlessly. We can align the elements <strong>edge-to-edge</strong> -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.flex-container</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.flex-item</span> <span class="token punctuation">{</span> <span class="token property">width</span><span class="token punctuation">:</span> 20%<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/9dqr8T1sBZ-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/9dqr8T1sBZ-780.webp 780w"><img alt="align elements edge-to-edge with flexbox" loading="lazy" decoding="async" src="https://bitsofco.de/img/9dqr8T1sBZ-780.png" width="780" height="247"></picture></p> <p>We can have space <strong>evenly distributed on both sides of the items</strong> -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.flex-container</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span><br> <span class="token property">justify-content</span><span class="token punctuation">:</span> space-around<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/pXaqkklOem-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/pXaqkklOem-780.webp 780w"><img alt="space around with flexbox" loading="lazy" decoding="async" src="https://bitsofco.de/img/pXaqkklOem-780.png" width="780" height="243"></picture></p> <p>We can even have the <strong>space distributed evenly between only the middle items</strong>, without dealing with <code>:first-child</code> or <code>:last-child</code> -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.flex-container</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span><br> <span class="token property">justify-content</span><span class="token punctuation">:</span> space-between<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/UmpLr-A_O--780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/UmpLr-A_O--780.webp 780w"><img alt="space-between with flexbox" loading="lazy" decoding="async" src="https://bitsofco.de/img/UmpLr-A_O--780.png" width="780" height="244"></picture></p> <h2 id="6-it-simplifies-complexity" tabindex="-1">6. It simplifies complexity <a class="header-anchor" href="https://bitsofco.de/6-reasons-to-start-using-flexbox/">#</a></h2> <p>The reason flexbox was created in the first place was for this very reason, to allow us to achieve the complex layouts we already create, in as little as one declaration.</p> <p>In the previous examples, I showed what could be done by styling the flex container alone. However, we can achieve much more fine-tuned styling by targeting the flex items. For example, a common layout for pricing tables is this -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/NNaKHD9g84-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/NNaKHD9g84-780.webp 780w"><img alt="pricing table layout" loading="lazy" decoding="async" src="https://bitsofco.de/img/NNaKHD9g84-780.png" width="780" height="654"></picture></p> <p>There are three divs, and the middle one is twice the width of the others. To achieve this layout using flexbox, we could write.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.flex-container</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span><br> <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">.flex-items:not(:nth-child(2))</span> <span class="token punctuation">{</span><br> <span class="token property">flex-grow</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 300px<span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">.flex-items:nth-child(2)</span> <span class="token punctuation">{</span><br> <span class="token property">flex-grow</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 350px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>I realise I'm late to the party, but thought there may still be other people out there that needed the extra nudge. Do you already use flexbox? If not, have you been convinced to give it a try?</p> Making KhaledBot - An Introduction to Slack Bots 2016-02-16T00:00:00Z https://bitsofco.de/creating-khaledbot-an-introduction-to-slack-bots/ <p>So I made a Slack bot… @khaledbot.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/rb36wbJBLo-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/rb36wbJBLo-780.webp 780w"><img alt="Khaledbot Website" loading="lazy" decoding="async" src="https://bitsofco.de/img/rb36wbJBLo-780.png" width="780" height="519"></picture></p> <p>You can get the bot yourself at <a href="https://khaledbot.com">khaledbot.com</a>, or download the source from my <a href="https://github.com/ireade/khaledbot">github repository</a>. As @khaledbot says,</p> <blockquote> <p>Learning is cool, but knowing is better, and I know the key to success.</p> </blockquote> <p>So here is how I created this Slack bot, and an introduction to creating bots for Slack in general.</p> <h2 id="1-create-a-bot-user-with-token" tabindex="-1">1. Create a Bot User with Token <a class="header-anchor" href="https://bitsofco.de/creating-khaledbot-an-introduction-to-slack-bots/">#</a></h2> <p>The first thing we will need to is <a href="https://my.slack.com/services/new/bot">create a new bot user</a> for the Slack team.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Y_t6fhn9Oj-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/Y_t6fhn9Oj-780.webp 780w"><img alt="Creating a slackbot" loading="lazy" decoding="async" src="https://bitsofco.de/img/Y_t6fhn9Oj-780.png" width="780" height="418"></picture></p> <p>All we need to do is choose a username for the bot, and a custom API Token for the bot will be generated. We can also add extra details like a real First and Last name for the bot, and a description of what the bot does.</p> <h2 id="2-create-a-new-node-js-project-with-botkit" tabindex="-1">2. Create a New Node.js Project with Botkit <a class="header-anchor" href="https://bitsofco.de/creating-khaledbot-an-introduction-to-slack-bots/">#</a></h2> <p>As I’m more familiar with JavaScript, I decided to create this bot using Node.js and Botkit. <a href="https://howdy.ai/botkit/">Botkit</a> is a framework, recommended by Slack, for building bots quickly and easily.</p> <p>We can install the botkit module via npm -</p> <pre><code>npm install botkit --save </code></pre> <p>Once botkit is installed, we can setup the bot. In the main index.js file -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> Botkit <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span>‘botkit’<span class="token punctuation">)</span> <span class="token comment">// require botkit module</span><br><span class="token keyword">var</span> token <span class="token operator">=</span> process<span class="token punctuation">.</span>env<span class="token punctuation">.</span><span class="token constant">SLACK_TOKEN</span> <span class="token comment">// get slack token passed as variable</span><br><br><span class="token comment">// Setup new slackbot with botkit</span><br><span class="token keyword">var</span> controller <span class="token operator">=</span> Botkit<span class="token punctuation">.</span><span class="token function">slackbot</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> debug<span class="token operator">:</span> <span class="token boolean">false</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><br><br><span class="token comment">// Check that token was passed</span><br><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>token<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>’<span class="token constant">SLACK_TOKEN</span> is required’<span class="token punctuation">)</span><br><span class="token punctuation">}</span><br><br><span class="token comment">// Start Slack’s Bot Real Time Messaging API (RTM)</span><br>controller<span class="token punctuation">.</span><span class="token function">spawn</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> token<span class="token operator">:</span> token<br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">startRTM</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span>bot<span class="token punctuation">,</span>payload</span><span class="token punctuation">)</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 punctuation">{</span><br> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span>err<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="3-start-bot" tabindex="-1">3. Start Bot <a class="header-anchor" href="https://bitsofco.de/creating-khaledbot-an-introduction-to-slack-bots/">#</a></h2> <p>While working on the bot, we can start it locally. From the terminal, while in the directory for the project, all we have to do is pass the token (from Step 1) as a variable while starting the main file -</p> <pre><code>SLACK_TOKEN=[slack-token-here] node index.js </code></pre> <h2 id="4-listening-for-events-and-keywords" tabindex="-1">4. Listening for Events and Keywords <a class="header-anchor" href="https://bitsofco.de/creating-khaledbot-an-introduction-to-slack-bots/">#</a></h2> <p>Once our bot is setup, we can start adding functionality to make it do what we want. Typically, bots respond to events that happen, e.g. a user joining a channel, or to keywords.</p> <h3 id="listening-for-events" tabindex="-1">Listening for Events <a class="header-anchor" href="https://bitsofco.de/creating-khaledbot-an-introduction-to-slack-bots/">#</a></h3> <p>There are several events that a bot can respond to. For example -</p> <ul> <li><code>bot_channel_join</code> - When the bot is invited to a new channel</li> <li><code>direct_mention</code> - When someone mentions the bot’s username directly, e.g. <code>@khaledbot: hi!</code></li> <li><code>direct_message</code> - When someone sends a direct (private) message to the bot</li> </ul> <p>For @khaledbot, I had it listen for when it was added to a channel. For example -</p> <pre class="language-js" tabindex="0"><code class="language-js">controller<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span><span class="token string">"bot_channel_join"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">bot<span class="token punctuation">,</span> message</span><span class="token punctuation">)</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></code></pre> <h3 id="listening-for-keywords" tabindex="-1">Listening for Keywords <a class="header-anchor" href="https://bitsofco.de/creating-khaledbot-an-introduction-to-slack-bots/">#</a></h3> <p>A bot can also be alerted when another member of the Slack team mentions a keyword in certain circumstances. To set this up, we specify two things -</p> <ol> <li>The <strong>keyword</strong> to listen for, e.g. &quot;hello&quot;</li> <li>The <strong>context</strong> in which the keyword was mentioned. For example, if it was a <code>direct_message</code>, a <code>direct_mention</code>, or <code>ambient</code> (when the bot is not directly mentioned, but is active in the channel)</li> </ol> <pre class="language-js" tabindex="0"><code class="language-js">controller<span class="token punctuation">.</span><span class="token function">hears</span><span class="token punctuation">(</span>array_of_keywords<span class="token punctuation">,</span> array_of_contexts<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">bot<span class="token punctuation">,</span> message</span><span class="token punctuation">)</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></code></pre> <p>For @khaledbot, whenever someone mentioned “khaled&quot;, the bot was alerted -</p> <pre class="language-js" tabindex="0"><code class="language-js">controller<span class="token punctuation">.</span><span class="token function">hears</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"khaled"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">"ambient"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">bot<span class="token punctuation">,</span> message</span><span class="token punctuation">)</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></code></pre> <h2 id="5-responding-to-events" tabindex="-1">5. Responding to Events <a class="header-anchor" href="https://bitsofco.de/creating-khaledbot-an-introduction-to-slack-bots/">#</a></h2> <p>When an event happens, we want the bot to do something in response. A bot can send a single reply using <code>bot.reply</code>. This function accepts two arguments - the message the bot is replying to, and the reply text.</p> <p>For @khaledbot, whenever someone mentioned “khaled”, the bot responds to them.</p> <pre class="language-js" tabindex="0"><code class="language-js">controller<span class="token punctuation">.</span><span class="token function">hears</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"khaled"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">"ambient"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">bot<span class="token punctuation">,</span> message</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">var</span> userID <span class="token operator">=</span> message<span class="token punctuation">.</span>user <span class="token comment">// the ID of the user that mentioned "khaled"</span><br> <span class="token keyword">var</span> user <span class="token operator">=</span> <span class="token string">"&lt;@"</span><span class="token operator">+</span>userID<span class="token operator">+</span><span class="token string">">"</span> <span class="token comment">// wrap around like this to create an @ mention of the user</span><br><br> <span class="token keyword">var</span> reply <span class="token operator">=</span> user<span class="token operator">+</span><span class="token string">" you spoke my name?"</span><span class="token punctuation">;</span> <span class="token comment">// create reply</span><br><br> bot<span class="token punctuation">.</span><span class="token function">reply</span><span class="token punctuation">(</span>message<span class="token punctuation">,</span> reply<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// send reply</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <p>We can use a series of these listens and replies to create the bot.</p> <h2 id="6-deploying-to-a-server" tabindex="-1">6. Deploying to a Server <a class="header-anchor" href="https://bitsofco.de/creating-khaledbot-an-introduction-to-slack-bots/">#</a></h2> <p>Once we have created our bot, we need it to live on a server to be online 24/7.</p> <p>For @khaledbot, I used <a href="https://beepboophq.com/">Beep Boop</a>, which is a service for easily deploying Slack bots. They offer free hosting for your bots for 24 hours (after that, you can restart the bot for another free 24 hour period or pay to maintain uptime).</p> <h2 id="and-we-re-live" tabindex="-1">And We're Live <a class="header-anchor" href="https://bitsofco.de/creating-khaledbot-an-introduction-to-slack-bots/">#</a></h2> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/tvBIInRlAB-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/tvBIInRlAB-720.gif 720w"><img alt="Demo of khaledbot" loading="lazy" decoding="async" src="https://bitsofco.de/img/tvBIInRlAB-720.webp" width="720" height="404"></picture></p> <p>You can get @khaledbot from <a href="https://khaledbot.com">khaledbot.com</a>. It's also on <a href="https://www.producthunt.com/tech/khaledbot">product hunt</a>. If you have any feedback on how I can improve on it, let me know. Bless up!</p> The One CSS Feature I Really Want 2016-02-09T00:00:00Z https://bitsofco.de/the-one-css-feature/ <p>There are many cutting edge CSS features/properties I wish were more widely supported.</p> <ul> <li>Variables</li> <li>Flexbox</li> <li>Transforms</li> <li>Animations</li> </ul> <p>The list can go on. However, there is one feature, introduced in CSS3, that I cannot wait to be supported in all, or at the least current versions of the major browsers. This is <code>@supports</code>.</p> <h2 id="the-magic-that-is-supports" tabindex="-1">The Magic That is @supports <a class="header-anchor" href="https://bitsofco.de/the-one-css-feature/">#</a></h2> <p>The <code>@supports</code> rule is a conditional group rule, similar to media queries, but for detecting if the current user agent supports a particular CSS property-value pair. Like media queries, if the statement given evaluates to true, the declarations within the group are applied.</p> <p>The <code>@supports</code> rule allows us to detect feature support in four ways -</p> <h3 id="1-detect-if-a-property-value-pair-is-supported" tabindex="-1">1. Detect if a property-value pair is supported <a class="header-anchor" href="https://bitsofco.de/the-one-css-feature/">#</a></h3> <p>The most basic way we can use the rule is to detect if a single CSS property-value pair is supported. For example, if we want to check if the current flexbox syntax is supported, we can write -</p> <pre class="language-css" tabindex="0"><code class="language-css"><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> flex <span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p>For browsers such as Internet Explorer 9, everything within the block will be ignored.</p> <h3 id="2-detect-if-all-of-several-property-value-pairs-are-supported" tabindex="-1">2. Detect if all of several property-value pairs are supported <a class="header-anchor" href="https://bitsofco.de/the-one-css-feature/">#</a></h3> <p>Sometimes, we need to use properties in combination. With the <code>and</code> rule, we can detect if a list of property-value pairs are supported by the browser.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token property">font-kerning</span><span class="token punctuation">:</span> normal <span class="token punctuation">)</span> <span class="token keyword">and</span><br> <span class="token punctuation">(</span> <span class="token property">font-feature-settings</span><span class="token punctuation">:</span> <span class="token string">"kern"</span> 1 <span class="token punctuation">)</span> <span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">font-kerning</span><span class="token punctuation">:</span> normal<span class="token punctuation">;</span><br> <span class="token property">font-feature-settings</span><span class="token punctuation">:</span> <span class="token string">"kern"</span> 1<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <h3 id="3-detect-if-either-of-several-property-value-pairs-are-supported" tabindex="-1">3. Detect if either of several property-value pairs are supported <a class="header-anchor" href="https://bitsofco.de/the-one-css-feature/">#</a></h3> <p>We can also detect if any of a list of properties are supported. This can be useful when trying to determine if a prefixed or unprefixed version of a property is supported.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token property">tranform</span><span class="token punctuation">:</span> <span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> -50%<span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token keyword">or</span><br> <span class="token punctuation">(</span> <span class="token property">-webkit-tranform</span><span class="token punctuation">:</span> <span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> -50%<span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">-webkit-tranform</span><span class="token punctuation">:</span> <span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> -50%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">tranform</span><span class="token punctuation">:</span> <span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> -50%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <h3 id="4-detect-if-a-property-value-pair-is-not-supported" tabindex="-1">4. Detect if a property-value pair is not supported <a class="header-anchor" href="https://bitsofco.de/the-one-css-feature/">#</a></h3> <p>Being able to detect if certain property-value pairs are supported is useful, but also being able to detect if a pair is not supported is even more useful. With the <code>not</code> rule, we can do just that.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@supports</span> <span class="token keyword">not</span> <span class="token punctuation">(</span> <span class="token property">display</span><span class="token punctuation">:</span> flex <span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> table<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p>Even more magically, we can detect if one property-value pair is supported <em>and</em> another is not supported. For example, we can target browsers that only support the old flexbox syntax.</p> <pre class="language-css" tabindex="0"><code class="language-css"><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> flexbox <span class="token punctuation">)</span><br> <span class="token keyword">and</span><br> <span class="token punctuation">(</span> <span class="token keyword">not</span> <span class="token punctuation">(</span> <span class="token property">display</span><span class="token punctuation">:</span> flex <span class="token punctuation">)</span> <span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">display</span><span class="token punctuation">:</span> flexbox<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p>😍😍😍</p> <h2 id="why-supports" tabindex="-1">Why @supports? <a class="header-anchor" href="https://bitsofco.de/the-one-css-feature/">#</a></h2> <p>The reason why I want this feature above all the other fun/crazy/cool features in CSS is because, currently, I can’t use those fun/crazy/cool features.</p> <p>If a browser, such as IE or Opera Mini, doesn’t support a feature, I simply cannot use it because a lot of the websites I build are for an audience that use those browsers.</p> <p>The <code>@supports</code> rule allow will allow me to have the best of both worlds - being able to write the fun, cutting-edge stuff for the browsers that support it, while writing the old tried-and-tested stuff for the browsers that don’t.</p> <h2 id="is-supports-supported" tabindex="-1">Is @supports Supported? <a class="header-anchor" href="https://bitsofco.de/the-one-css-feature/">#</a></h2> <p>The global support for <code>@supports</code> is currently at about 70%.</p> <p class="ciu_embed" data-feature="css-featurequeries" 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-featurequeries.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/css-featurequeries.png"> <img src="https://caniuse.bitsofco.de/image/css-featurequeries.jpg" alt="Data on support for the css-featurequeries feature across the major browsers from caniuse.com"> </picture> </p> <p>Even though <code>@supports</code> is currently supported by the majority of browsers, I can’t feel comfortable using it until at least all current browser versions support it. If it only works in some browsers, that sort of defeats the point of it.</p> <p>So I guess this is my open letter, pleading to those browsers, to support <code>@supports</code>. On the day that I see that all current versions of major browsers support this, this will be me.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/vAM_ckWNXQ-500.avif 500w"><source type="image/gif" srcset="https://bitsofco.de/img/vAM_ckWNXQ-500.gif 500w"><img alt="Celebration" loading="lazy" decoding="async" src="https://bitsofco.de/img/vAM_ckWNXQ-500.webp" width="500" height="244"></picture></p> <p>In the mean time, <a href="https://ireade.github.io/issupportssupportedyet/">is supports supported yet?</a></p> Responsive Images - The srcset and sizes Attributes 2016-02-02T00:00:00Z https://bitsofco.de/the-srcset-and-sizes-attributes/ <p>In the responsive web design revolution, images are one thing that have seemingly lagged behind. Until relatively recently, serving different images based on screen size or pixel density was not something that was done at all.</p> <p>The <code>&lt;picture&gt;</code> element shows a lot of promise in changing this. But whether the picture element is used at all, there are two attributes that are key in providing responsive images - <code>srcset</code> and the accompanying <code>sizes</code>.</p> <h2 id="the-srcset-attribute" tabindex="-1">The <code>srcset</code> attribute <a class="header-anchor" href="https://bitsofco.de/the-srcset-and-sizes-attributes/">#</a></h2> <p>The <code>srcset</code> attribute allows us to provide a set of images that can be potentially served by the browser. We, as the authors, provide a comma-separated list of images and the user agent determines which image to display depending on the particulars of the device.</p> <p>When listing the images, we provide two pieces of information about each image -</p> <ul> <li>The <strong>path</strong> to the image file</li> <li>The <strong>pixel density</strong> <em>or</em> <strong>width</strong> of the image</li> </ul> <p>To define <strong>pixel density</strong>, we add an <code>x</code> to the density number for the image. For example -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>one.png<span class="token punctuation">"</span></span><br> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>two.png 2x, three.png 3x, four.png 4x<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>The image defined with the <code>src</code> attribute is assumed to be <code>1x</code>.</p> <p>When the <code>srcset</code> attribute was first specified in 2012, we could only provide images of different pixel densities, as shown above. However, the 2014 Specification introduced the width value, which allows us to assign the width of the different images.</p> <p>To assign the width of the image, we add a <code>w</code> to the pixel width number for the image. For example -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>one.png<span class="token punctuation">"</span></span><br> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>two.png 100w, three.png 500w, four.png 1000w<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>Only when using the width version of the <code>srcset</code> attribute, can we accompany it with the <code>sizes</code> attribute.</p> <h2 id="the-sizes-attribute" tabindex="-1">The <code>sizes</code> attribute <a class="header-anchor" href="https://bitsofco.de/the-srcset-and-sizes-attributes/">#</a></h2> <p>The <code>sizes</code> attribute allows us to explicitly define the size the image should be according to a set of media conditions.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>one.png<span class="token punctuation">"</span></span><br> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>two.png 100w, three.png 500w, four.png 1000w<span class="token punctuation">"</span></span><br><br> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>&lt;media condition> &lt;width>,<br> &lt;media condition> &lt;width>,<br> &lt;optional default image width><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <h3 id="media-conditions" tabindex="-1">Media Conditions <a class="header-anchor" href="https://bitsofco.de/the-srcset-and-sizes-attributes/">#</a></h3> <p>A media condition is not exactly a media query. It is part of a media query. It doesn’t allow us to specify media types, e.g. <code>screen</code> or <code>print</code>, but accepts the condition we usually add to a media type.</p> <p>A valid media condition can be either -</p> <ul> <li>A plain media condition, e.g. <code>(min-width: 900px)</code></li> <li>A “not” media condition, e.g. <code>( not (orientation: landscape) )</code></li> <li>An “and” media condition, e.g. <code>(orientation: landscape) and (min-width: 900px)</code></li> <li>An “or” media condition, e.g. <code>( (orientation: portrait) or (max-width: 500px) )</code></li> </ul> <h3 id="width" tabindex="-1">Width <a class="header-anchor" href="https://bitsofco.de/the-srcset-and-sizes-attributes/">#</a></h3> <p>The <strong>width</strong> specified can be almost any length value, e.g. em, rem, pixels, and viewport width.</p> <p>However, percentage values are not allowed, &quot;to avoid confusion about what it would be relative to&quot;. The <code>vw</code> value is recommended as an alternative if a relative value is needed.</p> <h3 id="putting-it-together" tabindex="-1">Putting it Together <a class="header-anchor" href="https://bitsofco.de/the-srcset-and-sizes-attributes/">#</a></h3> <p>Putting the media condition and width together -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>one.png<span class="token punctuation">"</span></span><br> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>two.png 100w, three.png 500w, four.png 1000w<span class="token punctuation">"</span></span><br><br> <span class="token attr-name">sizes</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(min-width: 900px) 1000px,<br> (max-width: 900px) and (min-width: 400px) 50em,<br> ( not (orientation: portrait) ) 300px,<br> ( (orientation: landscape) or (min-width: 1000px) ) 50vw, <br> 100vw<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>If the media condition is true, the user agent will choose what image to serve at the size specified based on the different images defined in the <code>srcset</code> attribute.</p> <h2 id="support" tabindex="-1">Support <a class="header-anchor" href="https://bitsofco.de/the-srcset-and-sizes-attributes/">#</a></h2> <p>The <code>srcset</code> and <code>sizes</code> attributes are relatively well supported.</p> <p class="ciu_embed" data-feature="srcset" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false"> <picture> <source type="image/webp" srcset="https://caniuse.bitsofco.de/image/srcset.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/srcset.png"> <img src="https://caniuse.bitsofco.de/image/srcset.jpg" alt="Data on support for the srcset feature across the major browsers from caniuse.com"> </picture> </p> <p>For browsers that do not support the attributes, the image is fetched from the regular <code>src</code> attribute and displayed normally across all media conditions.</p> Creating an Embed for CanIUse 2016-01-26T00:00:00Z https://bitsofco.de/caniuse-embed/ <p>Like a lot of developers, I use <a href="https://caniuse.com">caniuse</a> <strong>a lot</strong>. Additionally, because of this blog, I frequently reference support for particular features and wanted a way of providing up-to-date information from caniuse within a post.</p> <p>So, I decided to create an (unofficial, but with permission) embed for caniuse. For example -</p> <p class="ciu_embed" data-feature="transforms2d" data-periods="future_1,current,past_1,past_2" data-accessible-colours="false"> <picture> <source type="image/webp" srcset="https://caniuse.bitsofco.de/image/transforms2d.webp"> <source type="image/png" srcset="https://caniuse.bitsofco.de/image/transforms2d.png"> <img src="https://caniuse.bitsofco.de/image/transforms2d.jpg" alt="Data on support for the transforms2d feature across the major browsers from caniuse.com"> </picture> </p> <p><a href="https://caniuse.bitsofco.de">Create an embed</a> | <a href="https://github.com/ireade/caniuse-embed/">View on Github</a></p> <h2 id="creating-the-embed" tabindex="-1">Creating the Embed <a class="header-anchor" href="https://bitsofco.de/caniuse-embed/">#</a></h2> <p>Having never done something like this before, here is how I created this embed.</p> <h3 id="1-hosting-the-embedded-document" tabindex="-1">1. Hosting the Embedded Document <a class="header-anchor" href="https://bitsofco.de/caniuse-embed/">#</a></h3> <p>The embed is an iframe that loads a particular document. This document is hosted via Github Pages at -</p> <pre><code>https://caniuse.bitsofco.de/embed/index.html </code></pre> <p>This url accepts 2 parameters -</p> <ul> <li><code>feat</code> - the ID of the feature on caniuse</li> <li><code>periods</code> - The browser versions to display, e.g. <code>current</code>, <code>past_1</code>, <code>future_1</code> etc.</li> </ul> <p>For example, for the &quot;CSS 2D Transforms&quot; feature above, the embed is hosted at -</p> <pre><code>https://caniuse.bitsofco.de/embed/index.html?feat=transforms2d&amp;periods=future_1,current,past_1,past_2 </code></pre> <h3 id="2-getting-the-relevant-data" tabindex="-1">2. Getting the Relevant Data <a class="header-anchor" href="https://bitsofco.de/caniuse-embed/">#</a></h3> <p>The caniuse data is made <a href="https://github.com/fyrd/caniuse">publicly available in raw json format</a> with a <a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0 licence</a>. Performing an <code>XMLHttpRequest</code> on full data file, we get the following object returned -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token punctuation">{</span><br> <span class="token literal-property property">agents</span><span class="token operator">:</span> Object <span class="token comment">// List of browsers</span><br> <span class="token literal-property property">cats</span><span class="token operator">:</span> Object <span class="token comment">// List of categories</span><br> <span class="token literal-property property">data</span><span class="token operator">:</span> Object <span class="token comment">// List of features</span><br> <span class="token literal-property property">eras</span><span class="token operator">:</span> Object<br> <span class="token literal-property property">statuses</span><span class="token operator">:</span> Object<br><span class="token punctuation">}</span></code></pre> <p>Based on the featured ID that was passed through the URL, we can get the relevant feature data -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> featureID <span class="token operator">=</span> location<span class="token punctuation">.</span>href<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">'?feat='</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br> featureID <span class="token operator">=</span> featureID<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">'&amp;periods='</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><span class="token function">loadJSON</span><span class="token punctuation">(</span>caniuseDataUrl<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">res</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">var</span> feature <span class="token operator">=</span> res<span class="token punctuation">.</span>data<span class="token punctuation">[</span>featureID<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>We now have the feature object with the following information -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token punctuation">{</span><br> <span class="token literal-property property">categories</span><span class="token operator">:</span> Array<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><br> <span class="token literal-property property">chrome_id</span><span class="token operator">:</span> <span class="token string">"6437640580628480"</span><br> <span class="token literal-property property">description</span><span class="token operator">:</span> <span class="token string">"Method of transforming an element including rotating, scaling, etc. Includes support for `transform` as well as `transform-origin` properties."</span><br> <span class="token literal-property property">firefox_id</span><span class="token operator">:</span> <span class="token string">""</span><br> <span class="token literal-property property">ie_id</span><span class="token operator">:</span> <span class="token string">"transforms"</span><br> <span class="token literal-property property">keywords</span><span class="token operator">:</span> <span class="token string">"transformation,translate,rotation,rotate,scale,css-transforms,transform-origin,transform:rotate,transform:scale"</span><br> <span class="token literal-property property">links</span><span class="token operator">:</span> Array<span class="token punctuation">[</span><span class="token number">7</span><span class="token punctuation">]</span><br> <span class="token literal-property property">notes</span><span class="token operator">:</span> <span class="token string">"The scale transform can be emulated in IE &lt; 9 using Microsoft's "</span>zoom<span class="token string">" extension, others are (not easily) possible using the MS Matrix filter"</span><br> <span class="token literal-property property">notes_by_num</span><span class="token operator">:</span> Object<br> <span class="token literal-property property">parent</span><span class="token operator">:</span> <span class="token string">""</span><br> <span class="token literal-property property">spec</span><span class="token operator">:</span> <span class="token string">"https://www.w3.org/TR/css3-2d-transforms/"</span><br> <span class="token literal-property property">stats</span><span class="token operator">:</span> Object <span class="token comment">// Support data for each browser</span><br> <span class="token literal-property property">status</span><span class="token operator">:</span> <span class="token string">"wd"</span><br> <span class="token literal-property property">title</span><span class="token operator">:</span> <span class="token string">"CSS3 2D Transforms"</span><br> <span class="token literal-property property">ucprefix</span><span class="token operator">:</span> <span class="token boolean">false</span><br> <span class="token literal-property property">usage_perc_a</span><span class="token operator">:</span> <span class="token number">0</span><br> <span class="token literal-property property">usage_perc_y</span><span class="token operator">:</span> <span class="token number">91.38</span><br> <span class="token literal-property property">webkit_id</span><span class="token operator">:</span> <span class="token string">""</span><br><span class="token punctuation">}</span></code></pre> <p>Once we have the relevant data, it just needs to be displayed in the document and styled appropriately.</p> <h3 id="3-using-a-placeholder-for-the-embed" tabindex="-1">3. Using a Placeholder for the Embed <a class="header-anchor" href="https://bitsofco.de/caniuse-embed/">#</a></h3> <p>To actually display the embed in an HTML document, you use a placeholder. For example -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>transforms2d<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 punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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=transforms2d<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Can I Use transforms2d?<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><br> Data on support for the transforms2d feature across the major browsers from caniuse.com.<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre> <p>By using a placeholder, we are able to provide a fallback (the content within the <code>&lt;p&gt;</code> tags) for situations where an iframe can't be loaded.</p> <p>For example, if you're reading this article via the email newsletter, I used an image of the embed as the fallback instead of just text.</p> <h2 id="4-loading-the-embedded-document" tabindex="-1">4. Loading the Embedded Document <a class="header-anchor" href="https://bitsofco.de/caniuse-embed/">#</a></h2> <p>Finally, to swap the placeholder for the actual iframe, I created a small script, which should be loaded onto any page with the embed.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</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://caniuse.bitsofco.de/caniuse-embed.min.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">&lt;/</span>script</span><span class="token punctuation">></span></span></code></pre> <p>This script does a few things -</p> <ul> <li>Finds all elements with the class <code>ciu_embed</code> and swaps the fallback content with the iframe.</li> <li>Determines what feature to load from the <code>data-feature</code> attribute and adds the relevant ID to the <code>feat</code> parameter</li> <li>Determines what browser versions to display from the <code>data-periods</code> attribute and adds the relevant information to the <code>periods</code> parameter</li> <li>Calculates the correct sizing of the <code>&lt;iframe&gt;</code> responsively depending on the contents</li> </ul> <p>You can view the source for the embed on <a href="https://github.com/ireade/caniuse-embed">github</a> or visit <a href="https://caniuse.bitsofco.de/">caniuse.bitsofco.de</a> to create an embed to use yourself.</p> The Effect of Importance and Origin on CSS Specificity 2016-01-19T00:00:00Z https://bitsofco.de/the-effect-of-importance-and-origin-on-specificity/ <p>When we think of the cascading nature of CSS, we usually think of specificity. Rules that are applied to an ID override rules that are applied to a class. However, specificity is not the only factor taken into account when determining which of two conflicting rule declarations should prevail.</p> <p>According to the <a href="https://www.w3.org/TR/2011/REC-CSS2-20110607/cascade.html#cascade">W3C Recommendation</a>, there are 4 stages to cascade -</p> <ol> <li><strong>Find all declarations</strong> that apply to the element and property in question, for the target media type. Declarations apply if the associated selector matches the element in question and the target medium matches the media list on all @media rules containing the declaration and on all links on the path through which the style sheet was reached.</li> </ol> <ul> <li>Sort according to <strong>importance</strong> and <strong>origin</strong>.</li> <li><strong>Sort rules with the same importance and origin by specificity of selector</strong>. More specific selectors will override more general ones. Pseudo-elements and pseudo-classes are counted as normal elements and classes, respectively.</li> <li>Finally, <strong>sort by order specified</strong>. If two declarations have the same weight, origin and specificity, the latter specified wins. Declarations in imported style sheets are considered to be before any declarations in the style sheet itself.</li> </ul> <p>Before the specificity of the rule is taken into account, the <strong>importance</strong> and <strong>origin</strong> of the rule is first considered.</p> <h2 id="importance-and-origin" tabindex="-1">Importance and Origin <a class="header-anchor" href="https://bitsofco.de/the-effect-of-importance-and-origin-on-specificity/">#</a></h2> <h3 id="declaration-origin" tabindex="-1">Declaration Origin <a class="header-anchor" href="https://bitsofco.de/the-effect-of-importance-and-origin-on-specificity/">#</a></h3> <p>The origin of the rule declared refers to where it comes from. There are three potential origins for a rule.</p> <p><strong>User agent</strong> - This is typically the browser. Browsers have their own “user agent stylesheet” with default styles. For example, in most browsers, a default style included is -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">head</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><strong>Author</strong> - This refers to the person creating the website. The person who writes the HTML and CSS processed by the user agent. Styles written by the author are included either inline or by linking to an external stylesheet.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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 value css language-css"><span class="token property">“color</span><span class="token punctuation">:</span></span></span></span> <span class="token attr-name">red;"</span><span class="token punctuation">></span></span>Lorem ipsum<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre> <p><strong>User</strong> - This refers to the person using the website. This person can include their own CSS to be used within a browser across all websites they visit. User defined styles are usually for accessibility concerns, for example -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">p</span> <span class="token punctuation">{</span> <span class="token property">font-size</span><span class="token punctuation">:</span> 18px<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">a</span> <span class="token punctuation">{</span> <span class="token property">text-decoration</span><span class="token punctuation">:</span> underline<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <h3 id="declaration-importance" tabindex="-1">Declaration Importance <a class="header-anchor" href="https://bitsofco.de/the-effect-of-importance-and-origin-on-specificity/">#</a></h3> <p>The importance of the rule is whether it has been specified as <strong>important</strong> or <strong>normal</strong>. By default, all rules are considered normal until we specify them to be important.</p> <p>A rule can be marked as <strong>important</strong> by using the <code>!important</code> rule -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">p</span> <span class="token punctuation">{</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 18px <span class="token important">!important</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="the-balance-between-importance-and-origin" tabindex="-1">The Balance Between Importance and Origin <a class="header-anchor" href="https://bitsofco.de/the-effect-of-importance-and-origin-on-specificity/">#</a></h2> <p>Once the importance and origin have been determined, conflicting rules relating to the same element are sorted according to the following -</p> <ol> <li>User agent declarations (lowest priority)</li> <li>User normal declarations</li> <li>Author normal declarations</li> <li>Author important declarations</li> <li>User important declarations (highest priority)</li> </ol> <p>This means that <strong>only multiple rules with the same level of importance and origin will move to the stage of determining specificity.</strong> If a rule does not have competing rule of the same importance and origin, its specificity is not considered.</p> <p>Take, for example, this paragraph.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“foo”</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“bar”</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre> <p><strong>Example 1.</strong> If there is an <code>!important</code> rule defined in the user stylesheet, it will prevail over more specific rules in the author stylesheet.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token comment">/* user defined stylesheet */</span><br><span class="token selector">p</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> orange <span class="token important">!important</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* this will prevail */</span><br><br><span class="token comment">/* author stylesheet */</span><br><span class="token selector">p</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> green<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">#bar</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> blue<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p><strong>Example 2.</strong> If there is an <code>!important</code> rule defined in the author stylesheet, it will prevail over more specific normal rules in the both stylesheets.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token comment">/* user defined stylesheet */</span><br><span class="token selector">#bar</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> orange<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><br><span class="token comment">/* author stylesheet */</span><br><span class="token selector">p</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> red <span class="token important">!important</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* this will prevail */</span><br><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> green<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">#bar</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> blue<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p><strong>Example 3.</strong> If there are two conflicting normal rules, the one in the author stylesheet will prevail, even if it is less specific.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token comment">/* user defined stylesheet */</span><br><span class="token selector">#bar</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> orange<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><br><span class="token comment">/* author stylesheet */</span><br><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> green<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/* this will prevail */</span></code></pre> <p>Because user defined stylesheets are not particularly common, the origin aspect is something us, as authors, need to be concerned about less. However, if we ever need to write a user agent stylesheet, it helps to know how to write rules that can/cannot be overridden.</p> Labelling Form Elements 2016-01-12T00:00:00Z https://bitsofco.de/labelling-form-elements/ <p>One context in which providing the correct labels for elements can be crucial is in HTML forms. Because users are interacting with forms more directly unlike with some UI elements, it is important that each form widget - <code>input</code>, <code>textarea</code>, <code>select</code> etc. - is correctly labelled.</p> <p>Depending on context, there are a number of ways to provide labels for form widgets.</p> <h2 id="the-name-and-id-attributes" tabindex="-1">The <code>name</code> and <code>id</code> attributes <a class="header-anchor" href="https://bitsofco.de/labelling-form-elements/">#</a></h2> <p>For most form widgets, it is important to include both <code>name</code> and <code>id</code> attributes.</p> <ul> <li>The <code>name</code> attribute provides the label the form widget is referenced by when submitted with the form data.</li> <li>The <code>id</code> attribute is used to reference the form widget when labelling using the <code>&lt;label for=&quot;&quot;&gt;</code> element.</li> </ul> <p>It is helpful to have both these labels be identical, so that the form widget is referenced in the same way on the front end as well as server-side.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</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>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>Besides the typical form widgets, it is useful to give the <code>&lt;form&gt;</code> element itself a unique <code>name</code> and/or <code>id</code>.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“myForm”</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>myForm<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token comment">&lt;!-- form widgets --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">></span></span></code></pre> <p>Besides providing a label for the form, it can be used to reference the form and check it's validity.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> myForm <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">"#myForm"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>myForm<span class="token punctuation">.</span><span class="token function">validate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Check the form values</span><br>myForm<span class="token punctuation">.</span><span class="token function">valid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Boolean value for if form is valid</span></code></pre> <h2 id="the-label-element-with-the-for-attribute" tabindex="-1">The <code>&lt;label&gt;</code> element with the <code>for</code> attribute <a class="header-anchor" href="https://bitsofco.de/labelling-form-elements/">#</a></h2> <p>The <code>&lt;label&gt;</code> element (with the <code>for</code> attribute) is the most commonly used way of labelling form widgets. Using the <code>for</code> attribute, we can reference the ID of the form widget that the label is for.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“username”</span><span class="token punctuation">></span></span>Your Username:<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“text”</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“username”</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>username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>We can also have multiple labels for one form widget.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“pwd”</span><span class="token punctuation">></span></span>Your Password:<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“password”</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“pwd”</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>pwd<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">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“pwd”</span><span class="token punctuation">></span></span>Your password must be at least 8 characters long.</code></pre> <h2 id="the-label-element-as-a-parent" tabindex="-1">The <code>&lt;label&gt;</code> element as a parent <a class="header-anchor" href="https://bitsofco.de/labelling-form-elements/">#</a></h2> <p>Another way of using the <code>&lt;label&gt;</code> element is to simply make it the parent of both the form widget and labelling text. Using this method, we do not need to specify the label’s <code>for</code> attribute or the widget's <code>id</code> attribute.</p> <p>All text within the element is taken as the label for the form widget.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span><br> Your Password:<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“password”</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“pwd”</span><span class="token punctuation">></span></span><br> Your password must be at least 8 characters long.<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span></code></pre> <h2 id="the-fieldset-and-legend-elements-for-related-widgets" tabindex="-1">The <code>&lt;fieldset&gt;</code> and <code>&lt;legend&gt;</code> elements for related widgets <a class="header-anchor" href="https://bitsofco.de/labelling-form-elements/">#</a></h2> <p>The <code>&lt;fieldset&gt;</code> element is used to group semantically related form widgets, and the <code>&lt;legend&gt;</code> element provides a label for the group. One common example of when this is used is when an address is broken up into multiple fields.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>fieldset</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>legend</span><span class="token punctuation">></span></span>You Address:<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>legend</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span><br> Street Address<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“text”</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>street_address<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">&lt;/</span>label</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span><br> City<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“text”</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>city<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">&lt;/</span>label</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span><br> State<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“text”</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>state<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">&lt;/</span>label</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span><br> Country<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“text”</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>country<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">&lt;/</span>label</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span><br> Postal Code<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“text”</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>postal_code<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">&lt;/</span>label</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>fieldset</span><span class="token punctuation">></span></span></code></pre> <p>Another great use for <code>&lt;fieldset&gt;</code> and <code>&lt;legend&gt;</code> is for <strong>radio inputs</strong>. Because each radio input has its own <code>&lt;label&gt;</code> element, we can’t use another <code>&lt;label&gt;</code> to group together the group of radio inputs.</p> <p>As a solution, we can use the <code>&lt;fieldset&gt;</code> element to group all the radio inputs together, and the <code>&lt;legend&gt;</code> element as a label for the group.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>fieldset</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>legend</span><span class="token punctuation">></span></span>Have you read the terms and conditions?<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>legend</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span><br> Yes<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>radio<span class="token punctuation">"</span></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>terms<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>yes<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">&lt;/</span>label</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">></span></span><br> No<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>radio<span class="token punctuation">"</span></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>terms<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no<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">&lt;/</span>label</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>fieldset</span><span class="token punctuation">></span></span></code></pre> <p>Using this method for radio/checkbox inputs is also great because some screen readers read the text within the legend before the label for each widget.</p> <p>In the example above, a screen reader may read aloud “Have you read the terms and conditions? Yes” and “Have you read the terms and conditions? No&quot; for each radio input.</p> <h2 id="the-label-attribute-for-optgroup" tabindex="-1">The <code>label</code> attribute for <code>&lt;optgroup&gt;</code> <a class="header-anchor" href="https://bitsofco.de/labelling-form-elements/">#</a></h2> <p>The <code>&lt;optgroup&gt;</code> element may be the only form element that using a <code>&lt;label&gt;</code> or <code>&lt;fieldset&gt;</code>+ <code>&lt;legend&gt;</code> elements is not a practical way to label it.</p> <p>Because of this, we have available the <code>label</code> attribute to provide a label for it.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>colours<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Choose a colour<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>select</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“colours”</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“colours”</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span><span class="token punctuation">></span></span>Black<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span><span class="token punctuation">></span></span>White<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>optgroup</span> <span class="token attr-name">label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“Primary”</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span><span class="token punctuation">></span></span>Red<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span><span class="token punctuation">></span></span>Yellow<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span><span class="token punctuation">></span></span>Blue<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>optgroup</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>optgroup</span> <span class="token attr-name">label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span>“Secondary”</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span><span class="token punctuation">></span></span>Green<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span><span class="token punctuation">></span></span>Purple<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span><span class="token punctuation">></span></span>Orange<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>optgroup</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>select</span><span class="token punctuation">></span></span></code></pre> <h2 id="the-aria-attributes" tabindex="-1">The <code>aria-*</code> attributes <a class="header-anchor" href="https://bitsofco.de/labelling-form-elements/">#</a></h2> <p>For cases where, because of the structure of our HTML, we cannot use any of these typical methods, we can use one of the ARIA labelling attributes.</p> <p>Use <code>aria-labelledby</code> to reference the ID of the labelling element -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</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>username-label<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Your Username<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></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>username-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">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p>Use <code>aria-label</code> to provide the label text itself -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Your Username<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>Use <code>aria-describedby</code> to reference the ID of an element that describes the form widget -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</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>username-label<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Username<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</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>username-description<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Your username must be at least 4 characters long.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>username<span class="token punctuation">"</span></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>username-label<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>username-description<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">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p>You can read more about using these ARIA attributes in my article on <a href="https://bitsofco.de/html-for-screen-readers-labelling-elements/">HTML For Screen Readers</a></p> WTF Opera Mini?! 2015-12-22T00:00:00Z https://bitsofco.de/wtf-opera-mini/ <p>According to StatCounter statistics, the global usage statistic for the Opera Mini mobile web browser is about 11.3%. However, in some developing countries such as Nigeria (where I live), <strong>this percentage goes up to 73%</strong>.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/C5zPk_xNUV-854.avif 854w"><source type="image/webp" srcset="https://bitsofco.de/img/C5zPk_xNUV-854.webp 854w"><img alt="StatCounter Mobile Browser Usage Statistics for Nigeria" loading="lazy" decoding="async" src="https://bitsofco.de/img/C5zPk_xNUV-854.png" width="854" height="500"></picture></p> <p>This is a huge problem.</p> <p>It’s a problem because Opera Mini today is basically like IE 8. It has terrible support for a lot of development features we take for granted. For example -</p> <ul> <li>2D transforms</li> <li><code>@font-face</code> Web fonts and, by extension, icon fonts</li> <li>Basically any styling for fonts including <code>line-height</code>, <code>text-decoration</code>, and using font families not native to the OS</li> </ul> <p>I only discovered this professional-life-altering fact about a week ago thanks to <a href="https://designbymobi.us">a friend</a>, and it’s been seriously racking my brain ever since.</p> <p>Although, as a developer, I personally dislike Opera Mini for this fact, there is a reason that people use it. For a particular kind of user, for example one with very limited data connection, it is very useful. Even though their experience may be diminished compared to if they were using a fully-fledged browser, all the feature-cutting works for their particular needs.</p> <h2 id="wtfoperamini-com" tabindex="-1">wtfoperamini.com <a class="header-anchor" href="https://bitsofco.de/wtf-opera-mini/">#</a></h2> <p>So, I decided to create this site, <a href="https://wtfoperamini.com">wtfoperamini.com</a>, which is a list of all the unsupported or partially supported development features on the Opera Mini mobile browser, and some crowdsourced workarounds for them.</p> <p>Although this is directed at Opera Mini, I hope for it to be a useful resource for anyone practicing progressive enhancement or graceful degradation.</p> <p><a href="https://wtfoperamini.com"><picture><source type="image/avif" srcset="https://bitsofco.de/img/jU8IybVRz9-2120.avif 2120w"><source type="image/webp" srcset="https://bitsofco.de/img/jU8IybVRz9-2120.webp 2120w"><img alt="Screenshot of wtfoperamini Website" loading="lazy" decoding="async" src="https://bitsofco.de/img/jU8IybVRz9-2120.png" width="2120" height="1206"></picture></a></p> <h2 id="workarounds" tabindex="-1">Workarounds <a class="header-anchor" href="https://bitsofco.de/wtf-opera-mini/">#</a></h2> <p>The aim of <a href="https://wtfoperamini.com">WTF Opera Mini?!</a> isn’t really Opera Mini bashing, but rather a way to crowdsource some workarounds people can use if they need to support the browser.</p> <p>For example, a particular pain point for me was the lack of support for 2D Transforms. To align elements both horizontally and vertically, I frequently use this magical snippet -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.centered-element</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">top</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token property">left</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token property">-webkit-transform</span><span class="token punctuation">:</span> <span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> -50%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">-ms-transform</span><span class="token punctuation">:</span> <span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> -50%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> -50%<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token selector">.centered-element-parent</span> <span class="token punctuation">{</span><br> <span class="token comment">/* make sure parent is positioned e.g. relative */</span><br><span class="token punctuation">}</span></code></pre> <p>This doesn’t work in Opera Mini because the <code>transform</code> property is not supported. So I’ve had to use different workarounds based on the situation. For example, one workaround is using tables and <code>vertical-align</code> -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.centered-element</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> table-cell<span class="token punctuation">;</span><br> <span class="token property">vertical-align</span><span class="token punctuation">:</span> middle<span class="token punctuation">;</span> <span class="token comment">/* vertical */</span><br> <span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span> <span class="token comment">/* horizontal */</span><br><span class="token punctuation">}</span><br><br><span class="token selector">.centered-element-parent</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> table<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h3 id="submit-a-workaround" tabindex="-1">Submit a Workaround <a class="header-anchor" href="https://bitsofco.de/wtf-opera-mini/">#</a></h3> <p>If you have ideas for workarounds for this or other features, you can submit one yourself. Follow the <a href="https://github.com/ireade/wtfoperamini#contribute">instructions for contributing</a>.</p> Controlling Typography in CSS 2015-12-15T00:00:00Z https://bitsofco.de/controlling-typography-in-css/ <p>When designing for print, we have a lot of control over how type is displayed. On the web, CSS gives us the ability to have almost as much fine-grained control over the way fonts are displayed and how they behave.</p> <p>There are about 18 (largely supported) typography-related CSS properties we can use to achieve almost any amount of nuance. These properties can be divided into three groups -</p> <ul> <li>Font properties</li> <li>Text properties</li> <li>Alignment &amp; Spacing properties</li> </ul> <h2 id="font-properties" tabindex="-1">Font Properties <a class="header-anchor" href="https://bitsofco.de/controlling-typography-in-css/">#</a></h2> <p>These properties control the font that is used and how the font is styled.</p> <ul> <li> <p><strong>font-family</strong> — The family of font to be used. This can be the name of a specific font, e.g. <code>Proxima Nova</code>, or a generic family name ,e.g. <code>monospace</code>.</p> </li> <li> <p><strong>font-weight</strong> — The boldness of the font. Accepts either a keyword value, e.g, <code>bolder</code>, or one of the accepted numbers, e.g. <code>100</code>, or <code>900</code>.</p> </li> <li> <p><strong>font-style</strong> — The style of the font within the font family. Can either be <code>normal</code>, <code>italic</code>, or <code>oblique</code>.</p> </li> <li> <p><strong>font-variant</strong> — The variation of the font family. Either <code>normal</code> or <code>small-caps</code>.</p> </li> <li> <p><strong>font-size</strong> — The size of the font. Accepts either absolute units, such as <code>12px</code>, or relative units such as <code>50%</code>. (<a href="https://bitsofco.de/css-font-sizing">More about font sizing</a>).</p> </li> </ul> <p>|Property|Values|Example|</p> <pre><code>&lt;tbody&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;2&quot;&gt; &lt;code&gt;font-family&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;serif&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-family: serif&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;Helvetica&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-family: Helvetica&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;3&quot;&gt; &lt;code&gt;font-weight&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;normal&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-weight: normal&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;700&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-weight: 700&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;lighter&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-weight: lighter&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;3&quot;&gt; &lt;code&gt;font-style&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;normal&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-style: normal&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;italic&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-style: italic&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;oblique&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-style: oblique&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;2&quot;&gt; &lt;code&gt;font-variant&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;normal&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-variant: normal&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;small-caps&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-variant: small-caps&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;5&quot;&gt; &lt;code&gt;font-size&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;12px&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-size: 12px&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;3em&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-size: 3em&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;50%&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-size: 50%&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;large&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-size: large&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;3vw&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; font-size: 3vw&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; </code></pre> <p>|---|---|---|</p> <h2 id="text-properties" tabindex="-1">Text Properties <a class="header-anchor" href="https://bitsofco.de/controlling-typography-in-css/">#</a></h2> <p>These properties control the behaviour of the text.</p> <ul> <li> <p><strong>text-align</strong> — Describes how inline elements within the text-aligned element are horizontally aligned.</p> </li> <li> <p><strong>text-decoration</strong> — Controls the decoration applied to the text. Mainly controls line decoration, e.g. <code>underline</code>, but can also create a text blinking affect with <code>blink</code> (for supporting browsers).</p> </li> <li> <p><strong>text-indent</strong> — Controls the indentation of the first line of text within the element.</p> </li> <li> <p><strong>text-transform</strong> — Specifies the format of capitalisation that applies to the text.</p> </li> <li> <p><strong>text-shadow</strong> — Describes the drop shadow to be applied to the text. Accepts four values - the horizontal position of the shadow (required), the vertical position (required), the blur radius, and the colour.</p> </li> <li> <p><strong>direction</strong> — Specifies the writing direction for the text.</p> </li> <li> <p><strong>unicode-bidi</strong> — Used to handle cases where an element contains both left-to-right text and right-to-left text.</p> </li> </ul> <p>|Property|Values|Example|</p> <pre><code>&lt;tbody&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;4&quot;&gt; &lt;code&gt;text-align&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;left&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-align: left&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;right&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-align: right&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;center&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-align: center&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;justify&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-align: justify&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;5&quot;&gt; &lt;code&gt;text-decoration&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;none&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-decoration: none&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;underline&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-decoration: underline&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;overline&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-decoration: overline&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;line-through&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-decoration: line-through&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;blink&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-decoration: blink&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;2&quot;&gt; &lt;code&gt;text-indent&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;10px&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-indent: 10px&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;10%&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-indent: 10%&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;4&quot;&gt; &lt;code&gt;text-transform&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;none&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-transform: none&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;capitalize&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-transform: capitalize&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;uppercase&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-transform: uppercase&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;lowercase&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-transform: lowercase&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;text-shadow&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;2px 2px red&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; text-shadow: 2px 2px red&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;2&quot;&gt; &lt;code&gt;direction&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;ltr&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; direction: ltr&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;rtl&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; direction: rtl&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;3&quot;&gt; &lt;code&gt;unicode-bidi&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;normal&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot;&gt; &lt;span style=&quot;display: block; unicode-bidi: normal; direction: ltr&quot;&gt;LTR: The quick brown fox jumped over the lazy dog&lt;/span&gt; </code></pre> <p>RTL: The quick brown fox jumped over the lazy dog</p> <pre><code> &lt;/tr&gt; </code></pre> <p>|---|---|---|</p> <h2 id="alignment-and-spacing-properties" tabindex="-1">Alignment &amp; Spacing Properties <a class="header-anchor" href="https://bitsofco.de/controlling-typography-in-css/">#</a></h2> <p>These properties control how each character or word is positioned within the characters or words around it.</p> <ul> <li> <p><strong>letter-spacing</strong> — Specifies the amount of spacing (or &quot;tracking&quot;) between each letter.</p> </li> <li> <p><strong>word-spacing</strong> — Specifies the amount of spacing between each word.</p> </li> <li> <p><strong>white-space</strong> — Describes how white space inside the element is handled.</p> </li> <li> <p><strong>word-wrap / overflow-wrap</strong> — Controls how long words that would not normally fit within the line are handled.</p> </li> <li> <p><strong>line-height</strong> — Specifies the line-height, i.e. the minimal height of line boxes, within the element.</p> </li> <li> <p><strong>vertical-align</strong> — Specifies the vertical positioning of the inline element within its parent. (<a href="https://bitsofco.de/the-vertical-align-property">More about vertical-align</a>)</p> </li> </ul> <p>|Property|Values|Example|</p> <pre><code>&lt;tbody&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;3&quot;&gt; &lt;code&gt;letter-spacing&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;normal&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; letter-spacing: normal&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;10px&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; letter-spacing: 10px&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;-2px&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; letter-spacing: -2px&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;3&quot;&gt; &lt;code&gt;word-spacing&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;normal&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; word-spacing: normal&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;10px&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; word-spacing: 10px&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;-10px&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; word-spacing: -10px&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;5&quot;&gt; &lt;code&gt;white-space&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;normal&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; white-space: normal&quot;&gt;The quick brown fox supercalifragilisticexpialidociously jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;pre&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; white-space: pre&quot;&gt;The quick brown fox supercalifragilisticexpialidociously jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;nowrap&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; white-space: nowrap&quot;&gt;The quick brown fox supercalifragilisticexpialidociously jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;pre-wrap&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; white-space: pre-wrap&quot;&gt;The quick brown fox supercalifragilisticexpialidociously jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;pre-line&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; white-space: pre-line&quot;&gt;The quick brown fox supercalifragilisticexpialidociously jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;2&quot;&gt; &lt;code&gt;word-wrap&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;normal&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; word-wrap: normal&quot;&gt;The quick brown fox The quick brown fox superdupersupercalifragilisticexpialidociouslysuperdupersupercalifragilisticexpialidociously jumped over the lazy dog jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;break-word&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; word-wrap: break-word;&quot;&gt;The quick brown fox The quick brown fox superdupersupercalifragilisticexpialidociouslysuperdupersupercalifragilisticexpialidociously jumped over the lazy dog jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;4&quot;&gt; &lt;code&gt;line-height&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;normal&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; line-height: normal&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;3&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; line-height: 3&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;20px&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; line-height: 20px&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;300%&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; line-height: 300%&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;property&quot; rowspan=&quot;10&quot;&gt; &lt;code&gt;vertical-align&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;baseline&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span style=&quot;display: block; vertical-align: baseline&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;sub&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span&gt;Baseline &lt;/span&gt;&lt;span style=&quot;vertical-align: sub&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;super&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span&gt;Baseline &lt;/span&gt;&lt;span style=&quot;vertical-align: super&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;top&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span&gt;Baseline &lt;/span&gt;&lt;span style=&quot;vertical-align: top&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;bottom&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span&gt;Baseline &lt;/span&gt;&lt;span style=&quot;vertical-align: bottom&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;text-top&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span&gt;Baseline &lt;/span&gt;&lt;span style=&quot;vertical-align: text-top&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;text-bottom&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span&gt;Baseline &lt;/span&gt;&lt;span style=&quot;vertical-align: text-bottom&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;middle&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span&gt;Baseline &lt;/span&gt;&lt;span style=&quot;vertical-align: middle&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;50%&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span&gt;Baseline &lt;/span&gt;&lt;span style=&quot;vertical-align: 50%&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td class=&quot;values&quot; rowspan=&quot;1&quot;&gt; &lt;code&gt;10px&lt;/code&gt; &lt;/td&gt; &lt;td class=&quot;example&quot; style=&quot;width: 50%; max-width: 400px&quot;&gt; &lt;span&gt;Baseline &lt;/span&gt;&lt;span style=&quot;vertical-align: 10px&quot;&gt;The quick brown fox jumped over the lazy dog&lt;/span&gt; &lt;/td&gt; &lt;/tr&gt; &lt;/tbody&gt; </code></pre> <p>|---|---|---|</p> The vertical-align Property 2015-12-08T00:00:00Z https://bitsofco.de/the-vertical-align-property/ <p>The <code>vertical-align</code> property controls the vertical positioning of elements within their parent. It applies to inline-level elements and table-cell elements. It has 10 possible values -</p> <ul> <li><code>baseline</code></li> <li><code>sub</code></li> <li><code>super</code></li> <li><code>text-top</code></li> <li><code>text-bottom</code></li> <li><code>middle</code></li> <li><code>top</code></li> <li><code>bottom</code></li> <li><code>&lt;length&gt;</code></li> <li><code>&lt;percentage&gt;</code></li> </ul> <h2 id="typography-units" tabindex="-1">Typography Units <a class="header-anchor" href="https://bitsofco.de/the-vertical-align-property/">#</a></h2> <p>The different values for the <code>vertical-align</code> property are dependent on specific typography units. So, to understand the values, we first need to understand these units of typography. There are 7 of these units that the values are base upon.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Y-pVYtJVt8-1611.avif 1611w"><source type="image/webp" srcset="https://bitsofco.de/img/Y-pVYtJVt8-1611.webp 1611w"><img alt="Typography Units" loading="lazy" decoding="async" src="https://bitsofco.de/img/Y-pVYtJVt8-1611.png" width="1611" height="535"></picture></p> <table> <thead> <tr> <th style="text-align:left">Colour</th> <th style="text-align:left">Unit</th> <th style="text-align:left">Description</th> </tr> </thead> <tbody> <tr> <td style="text-align:left"></td> <td style="text-align:left">baseline</td> <td style="text-align:left">The baseline for the font</td> </tr> <tr> <td style="text-align:left"></td> <td style="text-align:left">subscript baseline</td> <td style="text-align:left">The baseline for subscript text</td> </tr> <tr> <td style="text-align:left"></td> <td style="text-align:left">superscript baseline</td> <td style="text-align:left">The baseline for superscript text</td> </tr> <tr> <td style="text-align:left"></td> <td style="text-align:left">x height</td> <td style="text-align:left">The height of the letter “x” in the font</td> </tr> <tr> <td style="text-align:left"></td> <td style="text-align:left">line height</td> <td style="text-align:left">The vertical length of the line</td> </tr> <tr> <td style="text-align:left"></td> <td style="text-align:left">font top</td> <td style="text-align:left">The top-most part of the font</td> </tr> <tr> <td style="text-align:left"></td> <td style="text-align:left">font bottom</td> <td style="text-align:left">The bottom-most part of the font</td> </tr> </tbody> </table> <h2 id="the-values" tabindex="-1">The Values <a class="header-anchor" href="https://bitsofco.de/the-vertical-align-property/">#</a></h2> <p>The <code>vertical-align</code> property vertically aligns an inline or table-cell element according to these units of typography. Depending on whether the property is being applied to an <code>inline</code> or <code>table-cell</code> element, the values have slightly different meanings.</p> <h3 id="baseline" tabindex="-1">Baseline <a class="header-anchor" href="https://bitsofco.de/the-vertical-align-property/">#</a></h3> <p>The <code>baseline</code> value, for inline elements, aligns the <strong>current element’s baseline</strong> to the <strong>parent element’s baseline</strong>. In the example below, the box has the following styles -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <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><br> <span class="token property">height</span><span class="token punctuation">:</span> 50px<span class="token punctuation">;</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 50px<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> plum<span class="token punctuation">;</span><br> <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/ZYQmyl10uH-1742.avif 1742w"><source type="image/webp" srcset="https://bitsofco.de/img/ZYQmyl10uH-1742.webp 1742w"><img alt="Baseline Value for Inline Elements" loading="lazy" decoding="async" src="https://bitsofco.de/img/ZYQmyl10uH-1742.png" width="1742" height="344"></picture></p> <p>For table-cell elements, the value aligns the <strong>current cell’s content</strong> with the <strong>baseline of all other baseline-aligned cells</strong> in the same row. In the example below, all cells in the highlighted row have a value of baseline.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/9lf9rg0srN-1800.avif 1800w"><source type="image/webp" srcset="https://bitsofco.de/img/9lf9rg0srN-1800.webp 1800w"><img alt="Baseline Value for Table-Cell Elements" loading="lazy" decoding="async" src="https://bitsofco.de/img/9lf9rg0srN-1800.png" width="1800" height="480"></picture></p> <h3 id="sub" tabindex="-1">Sub <a class="header-anchor" href="https://bitsofco.de/the-vertical-align-property/">#</a></h3> <p>The <code>sub</code> value, for inline elements, aligns the <strong>current element’s baseline</strong> to the <strong>parent element’s subscript baseline</strong>.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/H9_KBuFyRN-1746.avif 1746w"><source type="image/webp" srcset="https://bitsofco.de/img/H9_KBuFyRN-1746.webp 1746w"><img alt="Sub Value for Inline Elements" loading="lazy" decoding="async" src="https://bitsofco.de/img/H9_KBuFyRN-1746.png" width="1746" height="320"></picture></p> <p>For table-cell elements, the value produces the same result as <code>baseline</code>.</p> <h3 id="super" tabindex="-1">Super <a class="header-anchor" href="https://bitsofco.de/the-vertical-align-property/">#</a></h3> <p>The <code>super</code> value, for inline elements, aligns the <strong>current element’s baseline</strong> to the <strong>parent element’s superscript baseline</strong>.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/KItarXzJzp-1744.avif 1744w"><source type="image/webp" srcset="https://bitsofco.de/img/KItarXzJzp-1744.webp 1744w"><img alt="Super Value for Inline Elements" loading="lazy" decoding="async" src="https://bitsofco.de/img/KItarXzJzp-1744.png" width="1744" height="354"></picture></p> <p>For table-cell elements, the value produces the same result as <code>baseline</code>.</p> <h3 id="text-top" tabindex="-1">Text-top <a class="header-anchor" href="https://bitsofco.de/the-vertical-align-property/">#</a></h3> <p>The <code>text-top</code> value, for inline elements, aligns the <strong>current element’s top</strong> to the <strong>parent element’s font top</strong>.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/XzSDIHGVX_-1758.avif 1758w"><source type="image/webp" srcset="https://bitsofco.de/img/XzSDIHGVX_-1758.webp 1758w"><img alt="Text-top Value for Inline Elements" loading="lazy" decoding="async" src="https://bitsofco.de/img/XzSDIHGVX_-1758.png" width="1758" height="324"></picture></p> <p>For table-cell elements, the value produces the same result as <code>baseline</code>.</p> <h3 id="text-bottom" tabindex="-1">Text-bottom <a class="header-anchor" href="https://bitsofco.de/the-vertical-align-property/">#</a></h3> <p>The <code>text-bottom</code> value, for inline elements, aligns the <strong>current element’s bottom</strong> to the <strong>parent element’s font bottom</strong>.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/AzBHZmjms8-1742.avif 1742w"><source type="image/webp" srcset="https://bitsofco.de/img/AzBHZmjms8-1742.webp 1742w"><img alt="Text-bottom Value for Inline Elements" loading="lazy" decoding="async" src="https://bitsofco.de/img/AzBHZmjms8-1742.png" width="1742" height="324"></picture></p> <p>For table-cell elements, the value produces the same result as <code>baseline</code>.</p> <h3 id="middle" tabindex="-1">Middle <a class="header-anchor" href="https://bitsofco.de/the-vertical-align-property/">#</a></h3> <p>The <code>middle</code> value, for inline elements, aligns the <strong>current element’s middle</strong> to the <strong>parent element’s middle</strong>. The middle of the parent element is calculated by taking the x height, halving it, and adding it to the baseline.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Q65cHSA34C-1748.avif 1748w"><source type="image/webp" srcset="https://bitsofco.de/img/Q65cHSA34C-1748.webp 1748w"><img alt="Middle Value for Inline Elements" loading="lazy" decoding="async" src="https://bitsofco.de/img/Q65cHSA34C-1748.png" width="1748" height="324"></picture></p> <p>For table-cell elements, the value aligns the <strong>current cell’s content</strong> with the <strong>center of the padding box of the row</strong>.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Mk4g1o0vmd-1798.avif 1798w"><source type="image/webp" srcset="https://bitsofco.de/img/Mk4g1o0vmd-1798.webp 1798w"><img alt="Middle Value for Table-Cell Elements" loading="lazy" decoding="async" src="https://bitsofco.de/img/Mk4g1o0vmd-1798.png" width="1798" height="468"></picture></p> <h3 id="top" tabindex="-1">Top <a class="header-anchor" href="https://bitsofco.de/the-vertical-align-property/">#</a></h3> <p>The <code>top</code> value, for inline elements, aligns the <strong>current element’s top</strong> to the <strong>top of the entire line</strong> that the current element sits within. This value is not necessarily related to font elements within the line.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/rx-Ns_R1y7-1740.avif 1740w"><source type="image/webp" srcset="https://bitsofco.de/img/rx-Ns_R1y7-1740.webp 1740w"><img alt="Top Value for Inline Elements" loading="lazy" decoding="async" src="https://bitsofco.de/img/rx-Ns_R1y7-1740.png" width="1740" height="434"></picture></p> <p>For table-cell elements, the value aligns the <strong>current cell’s content</strong> with the <strong>top padding edge of the row</strong>.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/S35I20biK8-1786.avif 1786w"><source type="image/webp" srcset="https://bitsofco.de/img/S35I20biK8-1786.webp 1786w"><img alt="Top Value for Table-Cell Elements" loading="lazy" decoding="async" src="https://bitsofco.de/img/S35I20biK8-1786.png" width="1786" height="456"></picture></p> <h3 id="bottom" tabindex="-1">Bottom <a class="header-anchor" href="https://bitsofco.de/the-vertical-align-property/">#</a></h3> <p>The <code>bottom</code> value, for inline elements, aligns the <strong>current element’s bottom</strong> to the <strong>bottom of the entire line</strong> that the current element sits within.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Z-yLaiGn-N-1746.avif 1746w"><source type="image/webp" srcset="https://bitsofco.de/img/Z-yLaiGn-N-1746.webp 1746w"><img alt="Bottom Value for Inline Elements" loading="lazy" decoding="async" src="https://bitsofco.de/img/Z-yLaiGn-N-1746.png" width="1746" height="456"></picture></p> <p>For table-cell elements, the value aligns the <strong>current cell’s content</strong> with the <strong>bottom padding edge of the row</strong>.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/75SZxBM0h1-1786.avif 1786w"><source type="image/webp" srcset="https://bitsofco.de/img/75SZxBM0h1-1786.webp 1786w"><img alt="Bottom Value for Table-Cell Elements" loading="lazy" decoding="async" src="https://bitsofco.de/img/75SZxBM0h1-1786.png" width="1786" height="444"></picture></p> <h3 id="length" tabindex="-1">Length <a class="header-anchor" href="https://bitsofco.de/the-vertical-align-property/">#</a></h3> <p>A length value, for inline elements, aligns the <strong>current element’s baseline</strong> to a length equal to the <strong>parent element’s baseline plus the specified length</strong>. In the example below, the pink box has the following styles -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">vertical-align</span><span class="token punctuation">:</span> 100px<span class="token punctuation">;</span><br> <span class="token comment">/* other styles */</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/QscswUp84k-1742.avif 1742w"><source type="image/webp" srcset="https://bitsofco.de/img/QscswUp84k-1742.webp 1742w"><img alt="Length Value for Inline Elements" loading="lazy" decoding="async" src="https://bitsofco.de/img/QscswUp84k-1742.png" width="1742" height="546"></picture></p> <p>For table-cell elements, the value produces the same result as <code>baseline</code>.</p> <h3 id="percentage" tabindex="-1">Percentage <a class="header-anchor" href="https://bitsofco.de/the-vertical-align-property/">#</a></h3> <p>A percentage value, for inline elements, aligns the <strong>current element’s baseline</strong> to a calculated length equal to the <strong>parent element’s baseline plus the specified percentage of the parent element’s line height</strong>. In the example below, the pink box has the following styles -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">vertical-align</span><span class="token punctuation">:</span> 200%<span class="token punctuation">;</span><br> <span class="token comment">/* other styles */</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Uzvhddu5Dn-1746.avif 1746w"><source type="image/webp" srcset="https://bitsofco.de/img/Uzvhddu5Dn-1746.webp 1746w"><img alt="Percentage Value for Inline Elements" loading="lazy" decoding="async" src="https://bitsofco.de/img/Uzvhddu5Dn-1746.png" width="1746" height="406"></picture></p> <p>For table-cell elements, the value produces the same result as <code>baseline</code>.</p> Tic Tac Toe (Revisited) 2015-12-01T00:00:00Z https://bitsofco.de/tic-tac-toe-revisited/ <p>One of the first projects I did on this blog was a <a href="https://bitsofco.de/tic-tac-toe">simple game of tic tac toe</a>. It was one of my first attempts at building something in JavaScript that wasn’t purely for UI purposes. Looking back at it now, it is functional, but could definitely be improved in many ways. So this is how, 8 months later, I approached the project.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/kO7k_1gkPD-1697.avif 1697w"><source type="image/webp" srcset="https://bitsofco.de/img/kO7k_1gkPD-1697.webp 1697w"><img alt="Tic Tac Toe Screenshot" loading="lazy" decoding="async" src="https://bitsofco.de/img/kO7k_1gkPD-1697.png" width="1697" height="836"></picture></p> <p><a href="https://github.com/ireade/tic-tac-toe-2">View Source</a> | <a href="https://ireade.github.io/tic-tac-toe-2/">Play the Game</a></p> <h2 id="project-overview" tabindex="-1">Project Overview <a class="header-anchor" href="https://bitsofco.de/tic-tac-toe-revisited/">#</a></h2> <p>The main problem with the way I handled the project the last time around was how I checked if each play was a winning play. Last time, I wrote out each possible win out by itself like this -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">if</span> <span class="token punctuation">(</span> sq1<span class="token punctuation">.</span><span class="token function">hasClass</span><span class="token punctuation">(</span><span class="token string">'X-play'</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> sq2<span class="token punctuation">.</span><span class="token function">hasClass</span><span class="token punctuation">(</span><span class="token string">'X-play'</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> sq3<span class="token punctuation">.</span><span class="token function">hasClass</span><span class="token punctuation">(</span><span class="token string">'X-play'</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">winAlert</span><span class="token punctuation">(</span><span class="token string">"X"</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 keyword">if</span> <span class="token punctuation">(</span> sq1<span class="token punctuation">.</span><span class="token function">hasClass</span><span class="token punctuation">(</span><span class="token string">'O-play'</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> sq2<span class="token punctuation">.</span><span class="token function">hasClass</span><span class="token punctuation">(</span><span class="token string">'O-play'</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> sq3<span class="token punctuation">.</span><span class="token function">hasClass</span><span class="token punctuation">(</span><span class="token string">'O-play'</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">winAlert</span><span class="token punctuation">(</span><span class="token string">"O"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><br><span class="token comment">// repeat for every possible combination</span></code></pre> <p>Old method</p> <p>This was <em>very</em> tedious and repetitive.</p> <p>This time around, I abstracted each of the winning combinations into an array of arrays, and wrote a function that would loop through each combination to determine if it was a winning one -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Winning combinations</span><br><span class="token keyword">var</span> wins <span class="token operator">=</span> <span class="token punctuation">[</span><br> <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 number">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br> <span class="token punctuation">[</span><span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">6</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br> <span class="token punctuation">[</span><span class="token number">7</span><span class="token punctuation">,</span> <span class="token number">8</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">7</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br> <span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">8</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br> <span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">6</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br> <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">9</span><span class="token punctuation">]</span><span class="token punctuation">,</span><br> <span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">,</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token number">7</span><span class="token punctuation">]</span><br><span class="token punctuation">]</span><span class="token punctuation">;</span><br><br><br><span class="token comment">/* Register if winning combination has happened */</span><br><span class="token keyword">function</span> <span class="token function">registerWin</span><span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> y<span class="token punctuation">,</span> z</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span><br> <span class="token comment">// All three winning squares played</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>x<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"played"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token boolean">true</span> <span class="token operator">&amp;&amp;</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>y<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"played"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token boolean">true</span> <span class="token operator">&amp;&amp;</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>z<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"played"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token boolean">true</span><br><br> <span class="token operator">&amp;&amp;</span><br><br> <span class="token comment">// All three squares played by the same player</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>x<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"player"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>y<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"player"</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>x<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"player"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>z<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"player"</span><span class="token punctuation">)</span><br> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> winner <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>x<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"player"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span> <span class="token comment">// end registerWin</span><br><br><br><span class="token comment">/* Check if there is a win */</span><br><span class="token keyword">function</span> <span class="token function">checkWin</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Loop through all winning combinations</span><br> <span class="token keyword">for</span> <span class="token punctuation">(</span>i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> wins<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 keyword">var</span> w <span class="token operator">=</span> <span class="token function">registerWin</span><span class="token punctuation">(</span>wins<span class="token punctuation">[</span>i<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> wins<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span> wins<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>w<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">alert</span><span class="token punctuation">(</span>winner<span class="token operator">+</span> <span class="token string">" won!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token function">addWinToTable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token function">clearBoard</span><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">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span>w <span class="token operator">&amp;&amp;</span> i <span class="token operator">===</span> <span class="token punctuation">(</span>wins<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">checkDraw</span><span class="token punctuation">(</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 comment">// end loop</span><br><span class="token punctuation">}</span> <span class="token comment">// end checkWin</span></code></pre> <p>Having the winning combinations abstracted in this way was also useful for making the program (playing as O) able to play tactically. I wrote a function that would look through the winning combinations to determine if any 2 of the 3 were played by the same player. If this was the case, then O should play the remaining square instead of something random.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">/* Funciton to check if 2 of 3 winning squares have been played by the same player */</span><br><br><span class="token keyword">function</span> <span class="token function">checkTacticalPlay</span><span class="token punctuation">(</span><span class="token parameter">x<span class="token punctuation">,</span> y<span class="token punctuation">,</span> z</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// x and y played</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span><br> <span class="token comment">// 2 out of the 3 winning squares</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>x<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"played"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token boolean">true</span> <span class="token operator">&amp;&amp;</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>y<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"played"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token boolean">true</span><br><br> <span class="token operator">&amp;&amp;</span><br><br> <span class="token comment">// Both squares played by the same player</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>x<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"player"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>y<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"player"</span><span class="token punctuation">)</span><br> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Return remaining square</span><br> <span class="token keyword">return</span> z<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token comment">// repeat for if x and z played</span><br> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>x<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"played"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token boolean">true</span> <span class="token operator">&amp;&amp;</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>z<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"played"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token boolean">true</span><br> <span class="token operator">&amp;&amp;</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>x<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"player"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>z<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"player"</span><span class="token punctuation">)</span><br> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> y<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><br> <span class="token comment">// repeat for if z and y played</span><br> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>z<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"played"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token boolean">true</span> <span class="token operator">&amp;&amp;</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>y<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"played"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token boolean">true</span><br> <span class="token operator">&amp;&amp;</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>z<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"player"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.square[data-square="'</span><span class="token operator">+</span>y<span class="token operator">+</span><span class="token string">'"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">data</span><span class="token punctuation">(</span><span class="token string">"player"</span><span class="token punctuation">)</span><br> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> x<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><br><span class="token punctuation">}</span> <span class="token comment">// end checkTacticalPlay</span></code></pre> <p>You can check out the full script for the game on <a href="https://github.com/ireade/tic-tac-toe-2">my github repository</a> or <a href="https://ireade.github.io/tic-tac-toe-2/">play the game here</a>.</p> <h3 id="becoming-more-dry" tabindex="-1">Becoming More DRY <a class="header-anchor" href="https://bitsofco.de/tic-tac-toe-revisited/">#</a></h3> <p>What I've learned from trying to rewrite this is that, over the past 8 months, I have developed a better habit of trying to write more DRY code (Don't Repeat Yourself). This new method involves a lot less repetition and is therefore easier to maintain.</p> <p>You never really notice how you've developed until you try to rewrite something you wrote before. If you have the time, I'd definitely suggest you give it a try (and let me know how you do)!</p> 3 Useful and Reusable SASS Mixins 2015-11-24T00:00:00Z https://bitsofco.de/3-useful-reusable-sass-mixins/ <p>When you’ve been writing CSS for a while, you come across certain snippets of code you write a lot. One of the greatest things about using SASS is the ability to abstract out these bits of code and instead insert a placeholder for them. This way, we don’t have to write out the full snippet every time we want to include it, and changes can be easily made to the snippet globally.</p> <p>I've put together a few of these mixins in a <a href="https://github.com/ireade/mixinbank">github repository</a>. But here are 3 of these mixins I wrote and now include in every project.</p> <h2 id="media-queries" tabindex="-1">Media Queries <a class="header-anchor" href="https://bitsofco.de/3-useful-reusable-sass-mixins/">#</a></h2> <p>In most projects, I tend to have a few standard breakpoints at which I change the layout for a number of elements. With SASS, I am able to abstract these out to make sure they are consistent across my CSS base.</p> <pre class="language-scss" tabindex="0"><code class="language-scss"><span class="token comment">// Define the breakpoints</span><br><span class="token property"><span class="token variable">$breakpoint-small</span></span><span class="token punctuation">:</span> 600px<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$breakpoint-med-small</span></span><span class="token punctuation">:</span> 960px<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$breakpoint-med</span></span><span class="token punctuation">:</span> 1175px<span class="token punctuation">;</span><br><br><span class="token keyword">@mixin</span> <span class="token function">screen</span><span class="token punctuation">(</span><span class="token variable">$size</span><span class="token punctuation">,</span> <span class="token property"><span class="token variable">$type</span></span><span class="token punctuation">:</span> max<span class="token punctuation">,</span> <span class="token property"><span class="token variable">$pixels</span></span><span class="token punctuation">:</span> <span class="token variable">$breakpoint-small</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">@if</span> <span class="token selector"><span class="token variable">$size</span> == 'small' </span><span class="token punctuation">{</span><br> <span class="token atrule"><span class="token rule">@media</span> screen <span class="token operator">and</span> <span class="token punctuation">(</span><span class="token variable">$type</span> <span class="token operator">+</span> <span class="token property">-width</span><span class="token punctuation">:</span> <span class="token variable">$breakpoint-small</span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token keyword">@content</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><br><br> <span class="token keyword">@else if</span> <span class="token selector"><span class="token variable">$size</span> == 'med-small' </span><span class="token punctuation">{</span><br> <span class="token atrule"><span class="token rule">@media</span> screen <span class="token operator">and</span> <span class="token punctuation">(</span><span class="token variable">$type</span> <span class="token operator">+</span> <span class="token property">-width</span><span class="token punctuation">:</span> <span class="token variable">$breakpoint-med-small</span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token keyword">@content</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><br><br> <span class="token keyword">@else if</span> <span class="token selector"><span class="token variable">$size</span> == 'med' </span><span class="token punctuation">{</span><br> <span class="token atrule"><span class="token rule">@media</span> screen <span class="token operator">and</span> <span class="token punctuation">(</span><span class="token variable">$type</span> <span class="token operator">+</span> <span class="token property">-width</span><span class="token punctuation">:</span> <span class="token variable">$breakpoint-med</span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token keyword">@content</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><br><br> <span class="token keyword">@else if</span> <span class="token selector"><span class="token variable">$size</span> == 'large' </span><span class="token punctuation">{</span><br> <span class="token atrule"><span class="token rule">@media</span> screen <span class="token operator">and</span> <span class="token punctuation">(</span><span class="token variable">$type</span> <span class="token operator">+</span> <span class="token property">-width</span><span class="token punctuation">:</span> <span class="token variable">$breakpoint-med</span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token keyword">@content</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><br><br> <span class="token keyword">@else if</span> <span class="token selector"><span class="token variable">$size</span> == 'custom' </span><span class="token punctuation">{</span><br> <span class="token atrule"><span class="token rule">@media</span> screen <span class="token operator">and</span> <span class="token punctuation">(</span><span class="token variable">$type</span> <span class="token operator">+</span> <span class="token property">-width</span><span class="token punctuation">:</span> <span class="token variable">$pixels</span> <span class="token operator">+</span> px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token keyword">@content</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token punctuation">}</span><br><br> <span class="token keyword">@else</span> <span class="token punctuation">{</span><br> <span class="token keyword">@content</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br><span class="token punctuation">}</span></code></pre> <p>To use the mixins, I write -</p> <pre class="language-scss" tabindex="0"><code class="language-scss"><span class="token selector">.foo </span><span class="token punctuation">{</span><br> <span class="token keyword">@include</span> <span class="token function">screen</span><span class="token punctuation">(</span>large<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 20%<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token keyword">@include</span> <span class="token function">screen</span><span class="token punctuation">(</span>med<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 40%<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token keyword">@include</span> <span class="token function">screen</span><span class="token punctuation">(</span>med-small<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 60%<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token keyword">@include</span> <span class="token function">screen</span><span class="token punctuation">(</span>small<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 80%<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token keyword">@include</span> <span class="token function">screen</span><span class="token punctuation">(</span>custom<span class="token punctuation">,</span> max<span class="token punctuation">,</span> 400<span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <h2 id="centering-elements" tabindex="-1">Centering Elements <a class="header-anchor" href="https://bitsofco.de/3-useful-reusable-sass-mixins/">#</a></h2> <p>We all know that centering elements in CSS can be a pain. There a many ways to centre an element depending on what you want to achieve. In many cases, I use the method of having the element be absolutely positioned and a combination of the offset and <code>transform</code> properties.</p> <pre class="language-scss" tabindex="0"><code class="language-scss"><span class="token keyword">@mixin</span> <span class="token function">center</span><span class="token punctuation">(</span><span class="token variable">$position</span><span class="token punctuation">)</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><br> <span class="token keyword">@if</span> <span class="token selector"><span class="token variable">$position</span> == 'vertical' </span><span class="token punctuation">{</span><br> <span class="token property">top</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token property">-webkit-transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>-50%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">-ms-transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>-50%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>-50%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token keyword">@else if</span> <span class="token selector"><span class="token variable">$position</span> == 'horizontal' </span><span class="token punctuation">{</span><br> <span class="token property">left</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token property">-webkit-transform</span><span class="token punctuation">:</span> <span class="token function">translateX</span><span class="token punctuation">(</span>-50%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">-ms-transform</span><span class="token punctuation">:</span> <span class="token function">translateX</span><span class="token punctuation">(</span>-50%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token keyword">@else if</span> <span class="token selector"><span class="token variable">$position</span> == 'both' </span><span class="token punctuation">{</span><br> <span class="token property">top</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token property">left</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token property">-webkit-transform</span><span class="token punctuation">:</span> <span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> -50%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">-ms-transform</span><span class="token punctuation">:</span> <span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> -50%<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translate</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> -50%<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 use this mixin, I write -</p> <pre class="language-scss" tabindex="0"><code class="language-scss"><span class="token selector">.foo </span><span class="token punctuation">{</span><br> <span class="token keyword">@include</span> <span class="token function">center</span><span class="token punctuation">(</span>both<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><span class="token selector">.foo-parent </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><span class="token punctuation">}</span></code></pre> <h2 id="hiding-and-showing-elements-with-a-transition" tabindex="-1">Hiding and Showing Elements with a Transition <a class="header-anchor" href="https://bitsofco.de/3-useful-reusable-sass-mixins/">#</a></h2> <p>Frequently, there are cases where I want to have an element hidden or displayed depending on a state and using a transition effect. Although there are <a href="https://bitsofco.de/hiding-elements-with-css">many ways to hide an element in CSS</a>, for these cases, I typically use the <code>visibility</code>/<code>opacity</code> method.</p> <p>I use the following mixin for this -</p> <pre class="language-scss" tabindex="0"><code class="language-scss"><span class="token keyword">@mixin</span> <span class="token function">fade</span><span class="token punctuation">(</span><span class="token variable">$type</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">@if</span> <span class="token selector"><span class="token variable">$type</span> == 'hide' </span><span class="token punctuation">{</span><br> <span class="token property">visibility</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span><br> <span class="token property">opacity</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">transition</span><span class="token punctuation">:</span> visibility 1s<span class="token punctuation">,</span> opacity 1s<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br> <span class="token keyword">@else if</span> <span class="token selector"><span class="token variable">$type</span> == 'show' </span><span class="token punctuation">{</span><br> <span class="token property">visibility</span><span class="token punctuation">:</span> visible<span class="token punctuation">;</span><br> <span class="token property">opacity</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span><br> <span class="token property">transition</span><span class="token punctuation">:</span> visibility 1s<span class="token punctuation">,</span> opacity 1s<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><br><span class="token punctuation">}</span></code></pre> <p>To use the mixin, I write -</p> <pre class="language-scss" tabindex="0"><code class="language-scss"><span class="token selector">.foo .bar </span><span class="token punctuation">{</span><br> <span class="token keyword">@include</span> <span class="token function">fade</span><span class="token punctuation">(</span><span class="token module-modifier keyword">hide</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><br><span class="token selector">.foo:hover .bar </span><span class="token punctuation">{</span><br> <span class="token keyword">@include</span> <span class="token function">fade</span><span class="token punctuation">(</span><span class="token module-modifier keyword">show</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> From Jekyll to Ghost 2015-11-17T00:00:00Z https://bitsofco.de/from-jekyll-to-ghost/ <p>Last week, I migrated this blog from using <a href="https://jekyllrb.com/">Jekyll</a> to generate a static site, to the hosted <a href="https://ghost.org">Ghost(Pro)</a>. There were a few reasons I wanted to do this -</p> <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> <p>Although Wordpress is the typical candidate for things like this, I don’t particularly enjoy using the Wordpress backend, especially their content editor. I like to write my posts in Markdown and I wanted something very stripped back.</p> <p>So, last week, I did the migration. Here is how the process went, and how I’ve found using Ghost for the past week.</p> <h2 id="the-migration-process" tabindex="-1">The Migration Process <a class="header-anchor" href="https://bitsofco.de/from-jekyll-to-ghost/">#</a></h2> <h3 id="step-1-creating-a-ghost-theme" tabindex="-1">Step 1 - Creating a Ghost Theme <a class="header-anchor" href="https://bitsofco.de/from-jekyll-to-ghost/">#</a></h3> <p>The first thing I had to do was create a new theme that would be compatible with Ghost. Although Jekyll and Ghost are both based on the <a href="https://handlebarsjs.com/">Handlebars templating language</a>, there are significant syntax differences I had to take into account.</p> <p>For example, in Jekyll, to loop through all the posts for the homepage, I would write -</p> <pre class="language-hbs" tabindex="0"><code class="language-hbs"><span class="token punctuation">{</span><span class="token punctuation">%</span> <span class="token variable">for</span> <span class="token variable">post</span> <span class="token variable">in</span> <span class="token variable">paginator</span><span class="token punctuation">.</span><span class="token variable">posts</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 variable">post</span> <span class="token variable">excerpt</span> <span class="token variable">here</span><br><span class="token punctuation">{</span><span class="token punctuation">%</span> <span class="token variable">endfor</span> <span class="token punctuation">%</span><span class="token punctuation">}</span></code></pre> <p>Whereas in Ghost, I would write -</p> <pre><code>{{#foreach posts}} // post excerpt here {{/foreach}} </code></pre> <p>I had created a theme for Ghost before (It’s called <a href="https://github.com/ireade/openwriter">OpenWriter</a> and available for free), so this stage wasn’t a major hassle.</p> <h3 id="step-2-exporting-the-posts" tabindex="-1">Step 2 - Exporting the Posts <a class="header-anchor" href="https://bitsofco.de/from-jekyll-to-ghost/">#</a></h3> <p>As far as I know, there is no way to export/import the posts between the two formats. So, I had to manually copy and paste each post from its raw markdown file in the Jekyll site to the Ghost markdown content editor.</p> <p><em>This was a tedious process.</em></p> <p>Additionally, because of the differences in markdown between the Jekyll and Ghost, I also had to edit most of the posts. For example, the way code snippets are highlighted between the two requires different syntax.</p> <p>In Jekyll, to add an html code snippet, I would write -</p> <pre class="language-hbs" tabindex="0"><code class="language-hbs"><span class="token punctuation">{</span><span class="token punctuation">%</span> <span class="token variable">highlight</span> <span class="token variable">html</span> <span class="token punctuation">%</span><span class="token punctuation">}</span><br> <span class="token punctuation">&lt;</span><span class="token variable">div</span> <span class="token variable">class</span><span class="token punctuation">=</span><span class="token string">"foo"</span><span class="token punctuation">></span><span class="token punctuation">&lt;</span><span class="token punctuation">/</span><span class="token variable">div</span><span class="token punctuation">></span><br><span class="token punctuation">{</span><span class="token punctuation">%</span> <span class="token variable">endhighlight</span> <span class="token punctuation">%</span><span class="token punctuation">}</span></code></pre> <p>Whereas in Ghost, I would write -</p> <pre><code>&lt;div class=&quot;foo&quot;&gt;&lt;/div&gt; </code></pre> <p>Luckily, I only had 37 posts, so it didn’t take a horrendous amount of time, and I tried to make use of search and replace where possible.</p> <p>As a side note, Ghost doesn't have in-built syntax highlighting, so I used <a href="https://prismjs.com/">Prism.js</a> in my theme.</p> <h3 id="step-3-transferring-the-domain" tabindex="-1">Step 3 - Transferring the Domain <a class="header-anchor" href="https://bitsofco.de/from-jekyll-to-ghost/">#</a></h3> <p>The next thing I had to do was transfer my domain to Ghost’s servers. This was a bit of a hurdle.</p> <p>The only way to use Ghost with a root domain is use an @ CNAME record, which most DNS provider’s don’t allow you to do. Because of this, I had to transfer the DNS management for bitsofco.de to <a href="https://www.cloudflare.com">Cloudflare</a>.</p> <p>Ghost provides some good <a href="https://support.ghost.org/root-domain-cloudflare/">documentation</a> on how to do this so it wasn’t particularly difficult.However, I did run into some issues following the steps and accidentally created an infinite redirect loop causing the site to be down for a while. I emailed the support team and they were really good in helping me fix the problem.</p> <h3 id="step-4-handling-301-redirects" tabindex="-1">Step 4 - Handling 301 Redirects <a class="header-anchor" href="https://bitsofco.de/from-jekyll-to-ghost/">#</a></h3> <p>The final thing take care of was the change in URL structure. With Jekyll, the url structure I set for my posts was -</p> <pre><code>**GHOST\_URL**/\[year\]/\[post-title\] </code></pre> <p>However, with Ghost, I was unable to have this same structure. So I instead opted for the simpler -</p> <pre><code>**GHOST\_URL**/\[post-title\] </code></pre> <p>Because I was using Ghost Pro, I did not have access to a <code>.htaccess</code> file to add the 301 redirect myself. I had to email the support team to help me do this. This went quickly and smoothly.</p> <p>Because of the change in URL structure, there was also the potential for many broken links. I discovered <a href="https://integrity.en.softonic.com/mac">Integrity</a>, a Mac app that crawls your site and looks for broken links. This was very helpful in allowing me to identify all the places within my posts I had linked to another post so I could change the link to the correct one.</p> <h2 id="ghost-vs-jekyll" tabindex="-1">Ghost vs Jekyll <a class="header-anchor" href="https://bitsofco.de/from-jekyll-to-ghost/">#</a></h2> <p>Now that everything is done and I have been using Ghost for the past week, here are my thoughts on the platform.</p> <h3 id="the-cons" tabindex="-1">The Cons <a class="header-anchor" href="https://bitsofco.de/from-jekyll-to-ghost/">#</a></h3> <p>To begin with, there were a few things I already knew I was giving up -</p> <ol> <li><strong>Flexibility</strong>. With Jekyll, I was able to do <em>anything</em> I wanted. I was really limited only by my technical abilities. With Ghost, I can only have features that are supported by the platform.</li> <li><strong>Stability</strong>. Ghost is still in beta. They could decide to scrap the platform tomorrow and, because I am on the hosted version, that would mean I would need to change to another platform.</li> </ol> <p>That being said, here are a couple other drawbacks for me -</p> <ol> <li><strong>No post scheduling (yet)</strong>. Although this is up next on their <a href="https://trello.com/c/ioKC8iN4/39-post-scheduling">roadmap</a>.</li> <li><strong>No support for tables in their flavour of Markdown</strong>. A relatively minor con, but it is quite annoying. Whenever I want to include a table in my post, I have to write an HTML table. And I like adding tables. For now, I still write my tables in Markdown and use an <a href="https://pandoc.org/try/">online markdown converter</a>.</li> </ol> <h3 id="the-pros" tabindex="-1">The Pros <a class="header-anchor" href="https://bitsofco.de/from-jekyll-to-ghost/">#</a></h3> <ol> <li><strong>Publishing is hassle-free</strong>. I will never mistakenly upload the wrong files via FTP again.</li> <li><strong>It’s completely open source</strong>. Because Ghost is open source, I could download a copy of the source code and run my own version on my own servers. So, if they did decide to stop developing it, I could still use whatever version I currently had forever.</li> <li><strong>Great community and support</strong>. The Ghost community is really helpful. They have an open <a href="https://ghost.org/slack/">Slack team</a> where you can quickly get answers to questions.</li> <li><strong>Great content editor</strong>. I <em>really</em> enjoy using it.</li> <li><strong>Great, simple CMS</strong>.</li> </ol> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/HvrXHLIoNq-2666.avif 2666w"><source type="image/webp" srcset="https://bitsofco.de/img/HvrXHLIoNq-2666.webp 2666w"><img alt="Screenshot of Ghost Backend" loading="lazy" decoding="async" src="https://bitsofco.de/img/HvrXHLIoNq-2666.png" width="2666" height="1318"></picture></p> <p> </p> <p>At this stage, for me, the pros outweigh the cons. I don't think Jekyll was the most scalable solution for a blog. I'm glad to be moving to a CMS and will see how Ghost works out in the long run.</p> <p>If you have a blog, I'd love to know what you use! Leave a comment below.</p> Using System Fonts in the Browser 2015-11-10T00:00:00Z https://bitsofco.de/using-system-fonts-in-the-browser/ <p>When defining a font family and style for our websites, we typically use the six font properties. This keeps things consistent between users of the website across various computer systems.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">p</span> <span class="token punctuation">{</span><br> <span class="token property">font-style</span><span class="token punctuation">:</span> normal<span class="token punctuation">;</span><br> <span class="token property">font-variant</span><span class="token punctuation">:</span> normal<span class="token punctuation">;</span><br> <span class="token property">font-weight</span><span class="token punctuation">:</span> 500<span class="token punctuation">;</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 13px<span class="token punctuation">;</span><br> <span class="token property">line-height</span><span class="token punctuation">:</span> 1.6<span class="token punctuation">;</span><br> <span class="token property">font-family</span><span class="token punctuation">:</span> Arial<span class="token punctuation">;</span><br> <br> <span class="token comment">/* shorthand */</span><br> <span class="token property">font</span><span class="token punctuation">:</span> normal normal 500 13px/1.6 Arial<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>In addition to acting as a shorthand for these six properties, the <code>font</code> property can also accept five keyword values. These values will set all six font properties of the element based on the various system fonts of the current user.</p> <p>These values are -</p> <ul> <li><code>caption</code></li> <li><code>icon</code></li> <li><code>menu</code></li> <li><code>message-box</code></li> <li><code>small-caption</code></li> <li><code>status-bar</code></li> </ul> <h2 id="the-font-families" tabindex="-1">The Font Families <a class="header-anchor" href="https://bitsofco.de/using-system-fonts-in-the-browser/">#</a></h2> <p>Each system typically has one default font family that is used. Depending on which of the keyword values are used (i.e. caption, icon etc), the font family typically stays the same, but the other font properties may differ.</p> <p>These are the default font families used across various systems.</p> <table> <thead> <tr> <th>System</th> <th>Font Family</th> </tr> </thead> <tbody> <tr> <td>Mac (Yosemite)</td> <td>Helvetica</td> </tr> <tr> <td>Mac (El Capitan)</td> <td>San Francisco</td> </tr> <tr> <td>Windows (XP and below)</td> <td>Arial</td> </tr> <tr> <td>Windows (Vista and above)</td> <td>Segoe UI</td> </tr> <tr> <td>Ubuntu</td> <td>Ubuntu</td> </tr> </tbody> </table> <h2 id="the-font-keyword-values" tabindex="-1">The <code>font</code> Keyword Values <a class="header-anchor" href="https://bitsofco.de/using-system-fonts-in-the-browser/">#</a></h2> <pre><code>font: caption | icon | menu | message-box | small-caption | status-bar </code></pre> <h3 id="caption" tabindex="-1">caption <a class="header-anchor" href="https://bitsofco.de/using-system-fonts-in-the-browser/">#</a></h3> <p>This refers to the font used by the user's system for &quot;captioned controls&quot;. For example, the font used for buttons.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/qU9S9NCtGy-1480.avif 1480w"><source type="image/webp" srcset="https://bitsofco.de/img/qU9S9NCtGy-1480.webp 1480w"><img alt="Button" loading="lazy" decoding="async" src="https://bitsofco.de/img/qU9S9NCtGy-1480.png" width="1480" height="242"></picture></p> <p>These are the font styles used for <code>caption</code> in Windows and Mac -</p> <table> <thead> <tr> <th>Font Properties</th> <th>Mac</th> <th>Windows</th> </tr> </thead> <tbody> <tr> <td>font-size</td> <td>13px</td> <td>16px</td> </tr> <tr> <td>font-style</td> <td>normal</td> <td>normal</td> </tr> <tr> <td>font-variant</td> <td>normal</td> <td>normal</td> </tr> <tr> <td>font-weight</td> <td>regular</td> <td>regular</td> </tr> </tbody> </table> <h3 id="icon" tabindex="-1">icon <a class="header-anchor" href="https://bitsofco.de/using-system-fonts-in-the-browser/">#</a></h3> <p>This refers to the font used to label icons.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/ejGaDinLy5-1450.avif 1450w"><source type="image/webp" srcset="https://bitsofco.de/img/ejGaDinLy5-1450.webp 1450w"><img alt="Font for Labelling Icons" loading="lazy" decoding="async" src="https://bitsofco.de/img/ejGaDinLy5-1450.png" width="1450" height="562"></picture></p> <table> <thead> <tr> <th>Font Properties</th> <th>Mac</th> <th>Windows</th> </tr> </thead> <tbody> <tr> <td>font-size</td> <td>13px</td> <td>16px</td> </tr> <tr> <td>font-style</td> <td>normal</td> <td>normal</td> </tr> <tr> <td>font-variant</td> <td>normal</td> <td>normal</td> </tr> <tr> <td>font-weight</td> <td>regular</td> <td>regular</td> </tr> </tbody> </table> <h3 id="menu" tabindex="-1">menu <a class="header-anchor" href="https://bitsofco.de/using-system-fonts-in-the-browser/">#</a></h3> <p>This refers to the font used in menus. For example, the font used in dropdown menus.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/D53bHrTwE4-1274.avif 1274w"><source type="image/webp" srcset="https://bitsofco.de/img/D53bHrTwE4-1274.webp 1274w"><img alt="Dropdown Menu" loading="lazy" decoding="async" src="https://bitsofco.de/img/D53bHrTwE4-1274.png" width="1274" height="410"></picture></p> <table> <thead> <tr> <th>Font Properties</th> <th>Mac</th> <th>Windows</th> </tr> </thead> <tbody> <tr> <td>font-size</td> <td>13px</td> <td>12px</td> </tr> <tr> <td>font-style</td> <td>normal</td> <td>normal</td> </tr> <tr> <td>font-variant</td> <td>normal</td> <td>normal</td> </tr> <tr> <td>font-weight</td> <td>regular</td> <td>regular</td> </tr> </tbody> </table> <h3 id="message-box" tabindex="-1">message-box <a class="header-anchor" href="https://bitsofco.de/using-system-fonts-in-the-browser/">#</a></h3> <p>This refers to the font used in dialog boxes.</p> <table> <thead> <tr> <th>Font Properties</th> <th>Mac</th> <th>Windows</th> </tr> </thead> <tbody> <tr> <td>All properties</td> <td>No style</td> <td>No style</td> </tr> </tbody> </table> <h3 id="small-caption" tabindex="-1">small-caption <a class="header-anchor" href="https://bitsofco.de/using-system-fonts-in-the-browser/">#</a></h3> <p>This refers to the font used for labelling small controls.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/0laJ6xJNHf-659.avif 659w"><source type="image/webp" srcset="https://bitsofco.de/img/0laJ6xJNHf-659.webp 659w"><img alt="Font for Small Caption" loading="lazy" decoding="async" src="https://bitsofco.de/img/0laJ6xJNHf-659.png" width="659" height="94"></picture></p> <table> <thead> <tr> <th>Font Properties</th> <th>Mac</th> <th>Windows</th> </tr> </thead> <tbody> <tr> <td>font-size</td> <td>11px</td> <td>12px</td> </tr> <tr> <td>font-style</td> <td>normal</td> <td>normal</td> </tr> <tr> <td>font-variant</td> <td>normal</td> <td>normal</td> </tr> <tr> <td>font-weight</td> <td>regular</td> <td>regular</td> </tr> </tbody> </table> <h3 id="status-bar" tabindex="-1">status-bar <a class="header-anchor" href="https://bitsofco.de/using-system-fonts-in-the-browser/">#</a></h3> <p>This refers to the font used in window status bars.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/1NNwtrfLPC-1714.avif 1714w"><source type="image/webp" srcset="https://bitsofco.de/img/1NNwtrfLPC-1714.webp 1714w"><img alt="Font for Status Bars" loading="lazy" decoding="async" src="https://bitsofco.de/img/1NNwtrfLPC-1714.png" width="1714" height="518"></picture></p> <table> <thead> <tr> <th>Font Properties</th> <th>Mac</th> <th>Windows</th> </tr> </thead> <tbody> <tr> <td>font-size</td> <td>10px</td> <td>12px</td> </tr> <tr> <td>font-style</td> <td>normal</td> <td>normal</td> </tr> <tr> <td>font-variant</td> <td>normal</td> <td>normal</td> </tr> <tr> <td>font-weight</td> <td>regular</td> <td>regular</td> </tr> </tbody> </table> <h2 id="using-system-fonts-in-the-browser" tabindex="-1">Using System Fonts in the Browser <a class="header-anchor" href="https://bitsofco.de/using-system-fonts-in-the-browser/">#</a></h2> <h3 id="provide-simple-fallbacks" tabindex="-1">Provide Simple Fallbacks <a class="header-anchor" href="https://bitsofco.de/using-system-fonts-in-the-browser/">#</a></h3> <p>These keywords can be extremely useful for providing a fallback for the font properties. Because they set all six of the properties in one word, we can use them as the default and selectively change particular properties if needed.</p> <p>For example writing this -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">p</span> <span class="token punctuation">{</span><br> <span class="token property">font</span><span class="token punctuation">:</span> caption<span class="token punctuation">;</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 18px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Is essentially equivalent to writing this -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">p</span> <span class="token punctuation">{</span><br> <span class="token property">font-family</span><span class="token punctuation">:</span> Helvetica<span class="token punctuation">;</span> <span class="token comment">/* If the user is on a Mac */</span><br> <span class="token property">font-style</span><span class="token punctuation">:</span> normal<span class="token punctuation">;</span><br> <span class="token property">font-weight</span><span class="token punctuation">:</span> regular<span class="token punctuation">;</span><br> <span class="token property">font-variant</span><span class="token punctuation">:</span> normal<span class="token punctuation">;</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 13px<span class="token punctuation">;</span> <span class="token comment">/* If the user is on a Mac */</span><br><br><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 18px<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>It is probably less useful for setting font families due to the ability to set reliable fallbacks with the <code>font-family</code> property. However, it can provide a useful default for the other font properties.</p> <h3 id="create-a-native-look" tabindex="-1">Create a Native Look <a class="header-anchor" href="https://bitsofco.de/using-system-fonts-in-the-browser/">#</a></h3> <p>Another use for these keywords to create the look of a native application. By using the same font the machine uses in the same scenarios, e.g. buttons, dropdown menus, you can make your website or application look native to the user’s system.</p> Understanding border-image 2015-11-03T00:00:00Z https://bitsofco.de/understanding-border-image/ <p>The <code>border-image</code> property is a relatively new property, which allows us to specify images to be used as borders instead of the typical border styles (border-style, border-color).</p> <p>The <code>border-image</code> property does not affect the <a href="https://bitsofco.de/controlling-the-box-model">box model border area</a>, which is controlled by the <code>border-width</code> property. However, the <code>border-image</code> property is still dependent on the existence of a border area. If <code>border-width</code> is set to <code>0</code>, the <code>border-image</code> property will not apply.</p> <p>The border image belongs to its own <strong>border image area</strong>, which is not the same as the border area, as we will see.</p> <blockquote> <p>The border image area, unlike the border area, extends aross the whole element, including the other box model areas (border area, padding area, content area).</p> </blockquote> <p>This allows us to use the border image (despite its name) as a background for the element, but also allows the border image to extend outside the border area.</p> <p>The <code>border-image</code> property is a shorthand for 5 properties controlling the border image -</p> <ul> <li><code>border-image-source</code></li> <li><code>border-image-slice</code></li> <li><code>border-image-outset</code></li> <li><code>border-image-repeat</code></li> <li><code>border-image-width</code></li> </ul> <p>The shorthand is writing in this order -</p> <pre><code>border-image: &lt;source&gt; &lt;slice&gt; &lt;width&gt; &lt;outset&gt; &lt;repeat&gt; </code></pre> <h2 id="border-image-source" tabindex="-1">border-image-source <a class="header-anchor" href="https://bitsofco.de/understanding-border-image/">#</a></h2> <table> <thead> <tr> <th>Values</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>url(path/to/image.jpg)</code></td> <td>The path to the image</td> </tr> <tr> <td><code>none</code></td> <td>No image</td> </tr> </tbody> </table> <p>The <code>border-image-source</code>property defines the path to the image to be used as the border. The image is used instead of any <code>border-style</code> value. The nature in which the image is applied depends on the other four border image properties.</p> <h2 id="border-image-slice" tabindex="-1">border-image-slice <a class="header-anchor" href="https://bitsofco.de/understanding-border-image/">#</a></h2> <table> <thead> <tr> <th>Values</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>number</td> <td>A number representing pixels (although the unit should not be written)</td> </tr> <tr> <td>percentage</td> <td>Percentage of the height/width of the full size image</td> </tr> <tr> <td><code>fill</code></td> <td>Image is not sliced</td> </tr> </tbody> </table> <p>The <code>border-image-slice</code> property defines how the image is sliced to create the border. The values specify how far, going from the outer edges of the image inward, before we slice the image. It does not control the width of the resulting border image, which is controlled by <code>border-image-width</code>, but rather it controls how the image is cropped to fit the border image area.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">border-image-slice</span><span class="token punctuation">:</span> 50<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/WyPQ1etBm9-601.avif 601w"><source type="image/webp" srcset="https://bitsofco.de/img/WyPQ1etBm9-601.webp 601w"><img alt="Border Image Slice 50" loading="lazy" decoding="async" src="https://bitsofco.de/img/WyPQ1etBm9-601.png" width="601" height="399"></picture></p> <p>We can alo specify different values for the four sides of the image -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">border-image-slice</span><span class="token punctuation">:</span> 50 100 200 100<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/5-MgZF-DLF-607.avif 607w"><source type="image/webp" srcset="https://bitsofco.de/img/5-MgZF-DLF-607.webp 607w"><img alt="Different Slice for 4 Sides" loading="lazy" decoding="async" src="https://bitsofco.de/img/5-MgZF-DLF-607.png" width="607" height="402"></picture></p> <p>By default, if a number or percentage value is given alone, the middle section of the image is removed to create a normal “border” effect. However, if we add the <code>fill</code> keyword, the image is not cut and fills up the background of the element. For example -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">border-image-slice</span><span class="token punctuation">:</span> 50 fill<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/iZ7VZFNB2r-1722.avif 1722w"><source type="image/webp" srcset="https://bitsofco.de/img/iZ7VZFNB2r-1722.webp 1722w"><img alt="Border Image Slice Fill" loading="lazy" decoding="async" src="https://bitsofco.de/img/iZ7VZFNB2r-1722.png" width="1722" height="1188"></picture></p> <h2 id="border-image-outset" tabindex="-1">border-image-outset <a class="header-anchor" href="https://bitsofco.de/understanding-border-image/">#</a></h2> <table> <thead> <tr> <th>Values</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>number</td> <td>A number representing multiples of the corresponsing <code>border-width</code></td> </tr> <tr> <td>length</td> <td>A pixel value (including px unit)</td> </tr> </tbody> </table> <p>The <code>border-image-outset</code> property defines how much the border image area extends outside the <a href="https://bitsofco.de/controlling-the-box-model">border area</a>.</p> <p>A value of 0 means that the border image area does not extend outside the border area at all. A value of 2 means the border image area extends outside the border area by 2 times the <code>border-width</code>. Similar to <code>border-image-slice</code>, we can specify values for each side of the border image.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">border-width</span><span class="token punctuation">:</span> 50px<span class="token punctuation">;</span><br> <span class="token property">border-image-outset</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span> <span class="token comment">/* border image area will map on border area exactly */</span><br> <span class="token property">border-image-outset</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span> <span class="token comment">/* extends outside by 100px (50px x 2) */</span><br> <span class="token property">border-image-outset</span><span class="token punctuation">:</span> 0 1 2 3<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/GoKnKX_C4K-760.avif 760w"><source type="image/gif" srcset="https://bitsofco.de/img/GoKnKX_C4K-760.gif 760w"><img alt="Border Image Outset" loading="lazy" decoding="async" src="https://bitsofco.de/img/GoKnKX_C4K-760.webp" width="760" height="525"></picture></p> <h2 id="border-image-repeat" tabindex="-1">border-image-repeat <a class="header-anchor" href="https://bitsofco.de/understanding-border-image/">#</a></h2> <table> <thead> <tr> <th>Values</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>stretch</code></td> <td>The image is stretched to fill the border image width</td> </tr> <tr> <td><code>repeat</code></td> <td>The image is tiled/repeated to fill the border image width</td> </tr> <tr> <td><code>round</code></td> <td>The image is tiled. If it does not fill the area with a whole number of tiles, the image is rescaled</td> </tr> <tr> <td><code>space</code></td> <td>The image is tiled. If it does not fill the area with a whole number of tiles, extra space is redistributed between the tiles</td> </tr> </tbody> </table> <p>The <code>border-image-repeat</code> property defines how the image should be scaled or tiled if it is not big enough to cover the full border area. Here is how the different values handle the same image -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/vRNfGh6TxK-760.avif 760w"><source type="image/gif" srcset="https://bitsofco.de/img/vRNfGh6TxK-760.gif 760w"><img alt="Border Image Repeat" loading="lazy" decoding="async" src="https://bitsofco.de/img/vRNfGh6TxK-760.webp" width="760" height="462"></picture></p> <p>We can specify a different behaviour for the vertical sides and for the horizontal sides of the border image area.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">border-image-repeat</span><span class="token punctuation">:</span> stretch repeat<span class="token punctuation">;</span> <span class="token comment">/* horizontal vertical*/</span><br><span class="token punctuation">}</span></code></pre> <h2 id="border-image-width" tabindex="-1">border-image-width <a class="header-anchor" href="https://bitsofco.de/understanding-border-image/">#</a></h2> <table> <thead> <tr> <th>Values</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>number</td> <td>A number representing multiples of the corresponsing <code>border-width</code></td> </tr> <tr> <td>percentage</td> <td>Percentage of the border image area</td> </tr> <tr> <td>length</td> <td>A pixel value (including px unit)</td> </tr> </tbody> </table> <p>The <code>border-image-width</code> property defines the actual width of the border image. Although, by default, the width is the same as the <code>border-width</code>, the width of the border image can actually be more or less. We can specify one value for all four sides, or specify each side’s value.</p> <p>If we use a number value, this represents multiples of the corresponding <code>border-width</code>. For example -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">border-width</span><span class="token punctuation">:</span> 50px<span class="token punctuation">;</span><br> <span class="token property">border-image-outset</span><span class="token punctuation">:</span> 3<span class="token punctuation">;</span><br> <span class="token property">border-image-width</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span> <span class="token comment">/* = 100px */</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/mAtSZlayln-1101.avif 1101w"><source type="image/webp" srcset="https://bitsofco.de/img/mAtSZlayln-1101.webp 1101w"><img alt="Border Image Width with Length Value" loading="lazy" decoding="async" src="https://bitsofco.de/img/mAtSZlayln-1101.png" width="1101" height="521"></picture></p> <p>If we specify a percentage value, this is calculated based on the border image area. This is made up of the whole element area plus any outset specified by the <code>border-image-outset</code>.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span><br> <span class="token property">border-width</span><span class="token punctuation">:</span> 50px<span class="token punctuation">;</span><br> <span class="token property">border-image-outset</span><span class="token punctuation">:</span> 3<span class="token punctuation">;</span><br> <span class="token property">border-image-width</span><span class="token punctuation">:</span> 10%<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Ugw4WL9ZNK-1109.avif 1109w"><source type="image/webp" srcset="https://bitsofco.de/img/Ugw4WL9ZNK-1109.webp 1109w"><img alt="Border Image Width with Percentage Value" loading="lazy" decoding="async" src="https://bitsofco.de/img/Ugw4WL9ZNK-1109.png" width="1109" height="518"></picture></p> How Positioning CSS Properties Interact 2015-10-27T00:00:00Z https://bitsofco.de/how-positioning-css-properties-interact/ <p>When positioning elements, there are four CSS properties we use - <code>display</code>, <code>position</code>, <code>float</code>, and the offset properties (<code>top</code>, <code>right</code>, <code>bottom</code> and <code>left</code>).</p> <p>However, not all the property-value combinations for these properties can co-exist within the same element. There are specific property-value combinations which will override others. These combinations are -</p> <ul> <li><code>display: none</code></li> <li><code>position: absolute</code> or <code>position: fixed</code></li> <li><code>float: left</code> or <code>float: right</code></li> <li><code>position: static</code></li> </ul> <p>Here is how these property-value combinations interact with the other positioning CSS properties.</p> <h2 id="display-none" tabindex="-1">display: none <a class="header-anchor" href="https://bitsofco.de/how-positioning-css-properties-interact/">#</a></h2> <p>When the display property on an element is set to none, none of the other positioning properties apply because <a href="https://bitsofco.de/controlling-the-box-model">the box model</a> is never generated.</p> <pre><code>.foo { display: none; /* None of these apply */ position: absolute; float: left; top: 10px; } </code></pre> <h2 id="position-absolute-or-fixed" tabindex="-1">position: absolute (or fixed) <a class="header-anchor" href="https://bitsofco.de/how-positioning-css-properties-interact/">#</a></h2> <p>Except where display is set to none, if position is set to absolute or fixed, the following applies -</p> <h3 id="float" tabindex="-1">float <a class="header-anchor" href="https://bitsofco.de/how-positioning-css-properties-interact/">#</a></h3> <p>Whatever value is set for the float property is overriden, and the computed value for float is automatically equal to <code>none</code>.</p> <pre><code>.foo { position: absolute; float: left; /* ignored, computed value is none */ } </code></pre> <h3 id="display" tabindex="-1">display <a class="header-anchor" href="https://bitsofco.de/how-positioning-css-properties-interact/">#</a></h3> <p>Depending on which value is specified for this property, the computed value may be overriden. The computed value for the display property is set according to the following table.</p> <table> <thead> <tr> <th>Specified Value</th> <th>Computed Value</th> </tr> </thead> <tbody> <tr> <td><code>inline</code>, <code>inline-block</code>, <code>table-row-group</code>, <code>table-column</code>, <code>table-column-group</code>, <code>table-header-group</code>, <code>table-footer-group</code>, <code>table-row</code>, <code>table-cell</code>, <code>table-caption</code></td> <td><code>block</code></td> </tr> <tr> <td><code>inline-table</code></td> <td><code>table</code></td> </tr> <tr> <td>Any other values</td> <td>Same as specified</td> </tr> </tbody> </table> <p>In the example below, there will be no difference between <code>.foo</code> and <code>.bar</code>.</p> <pre><code>.foo { position: fixed; display: inline-block; /* ignored, computed value is block */ } .bar { position: fixed; display: block; } </code></pre> <h2 id="float-left-or-right" tabindex="-1">float: left (or right) <a class="header-anchor" href="https://bitsofco.de/how-positioning-css-properties-interact/">#</a></h2> <p>Except for where display is set to none, or position is set to absolute/fixed, if an element is floated, the following applies -</p> <h3 id="display-1" tabindex="-1">display <a class="header-anchor" href="https://bitsofco.de/how-positioning-css-properties-interact/">#</a></h3> <p>Similar to what happens when position is set to absolute/fixed, when an element is floated, the display properties are set according to the table above.</p> <p>In the example below, there will be no difference between <code>.foo</code> and <code>.bar</code>.</p> <pre><code>.foo { float: left; display: inline-block; /* ignored, computed value is block */ } .bar { float: left; display: block; } </code></pre> <h2 id="position-static" tabindex="-1">position: static <a class="header-anchor" href="https://bitsofco.de/how-positioning-css-properties-interact/">#</a></h2> <p>Except for the previous property-value combinations, if an element has a position of static, the following applies -</p> <h3 id="offset-properties" tabindex="-1">offset properties <a class="header-anchor" href="https://bitsofco.de/how-positioning-css-properties-interact/">#</a></h3> <p>When an element is set as position: static, the offset properties - top right bottom left - do not apply.</p> <pre><code>.foo { position: static; top: 50px; /* does not apply */ } </code></pre> How z-index Works 2015-10-20T00:00:00Z https://bitsofco.de/how-z-index-works/ <p>Although it may not be immediately obvious, the elements in an HTML document are generated in three dimensions. Besides being aligned on the x- and y- axes, elements can lie on the z-axis, which controls their position in the third dimension.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/z_n-iZrS-o-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/z_n-iZrS-o-780.webp 780w"><img alt="The 3 Dimensions" loading="lazy" decoding="async" src="https://bitsofco.de/img/z_n-iZrS-o-780.jpeg" width="780" height="387"></picture></p> <p>Properties such as margin, float, and the offset properties control how the element sits on the x- and y- axes. The <code>z-index</code> property solely controls how elements are arranged on this z-axis.</p> <h2 id="the-z-index-property" tabindex="-1">The z-index Property <a class="header-anchor" href="https://bitsofco.de/how-z-index-works/">#</a></h2> <p>The <code>z-index</code> property specifies two things -</p> <ul> <li>The <strong><a href="https://bitsofco.de/how-z-index-works/">stacking level</a></strong> of the current element</li> <li>If the current element establishes a new <strong><a href="https://bitsofco.de/how-z-index-works/">stacking context</a></strong></li> </ul> <p>The property only applies to positioned elements. That is, elements that have a <code>position</code> of <code>relative</code>, <code>absolute</code>, or <code>fixed</code>.</p> <p>There are three potential values for the <code>z-index</code> property -</p> <table> <thead> <tr> <th>Value</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>auto</code></td> <td>Sets the stacking level to 0 and does not establish a new stacking context</td> </tr> <tr> <td><code>&lt;integer&gt;</code></td> <td>Sets the stacking level to the integer <strong>and</strong> establishes a new stacking context</td> </tr> <tr> <td><code>inherit</code></td> <td>Sets the stacking level to the same as it’s parent element and does not establish a new stacking context</td> </tr> </tbody> </table> <pre><code>z-index: auto | &lt;integer&gt; | inherit </code></pre> <h2 id="stacking-level" tabindex="-1">Stacking Level <a class="header-anchor" href="https://bitsofco.de/how-z-index-works/">#</a></h2> <p>The stacking level is the value on the z-axis that the current element is on. Higher numbers indicate that the element is higher on the stack of elements and closer to the surface of the screen.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/zeDmwlyRbq-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/zeDmwlyRbq-780.webp 780w"><img alt="Stacking Level" loading="lazy" decoding="async" src="https://bitsofco.de/img/zeDmwlyRbq-780.png" width="780" height="413"></picture></p> <p>If not specified by the z-index property, the stacking level of an element is established according to it’s arrangement in the document tree. Elements that are declared later in the document have a higher stacking level by default.</p> <h3 id="calculating-stacking-level" tabindex="-1">Calculating Stacking Level <a class="header-anchor" href="https://bitsofco.de/how-z-index-works/">#</a></h3> <p>In addition to the specified <code>z-index</code>, the stacking level of an element is controlled by a number of factors. Elements are stacked in the following order.</p> <table> <thead> <tr> <th>Position</th> <th>Description</th> <th>CSS</th> </tr> </thead> <tbody> <tr> <td>1 (bottom)</td> <td>The element forming the stacking context</td> <td><code>z-index: &lt;integer&gt;</code></td> </tr> <tr> <td>2</td> <td>Child elements with negative stack levels</td> <td>`z-index: <negative integer="">``position: relative</negative></td> </tr> <tr> <td>3</td> <td>In-flow, non-inline, non-positioned child elements</td> <td><code>display: /* not inline */``position: static</code></td> </tr> <tr> <td>4</td> <td>Non-positioned floating child elements</td> <td>`float: left</td> </tr> <tr> <td>5</td> <td>In-flow inline, non-positioned child elements</td> <td><code>display: /* inline */``position: static</code></td> </tr> <tr> <td>6</td> <td>Child elements with stacking level of 0</td> <td>`z-index: auto</td> </tr> <tr> <td>7 (top)</td> <td>Child elements with positive stack levels</td> <td>`z-index: <positive integer="">``position: relative</positive></td> </tr> </tbody> </table> <h2 id="stacking-context" tabindex="-1">Stacking Context <a class="header-anchor" href="https://bitsofco.de/how-z-index-works/">#</a></h2> <p>When we specify the stacking level for an element using the <code>z-index</code> property, we are not always specifying the stacking level of the element in relation to every other element on the page. <strong>The element’s stacking level is only in relation to its stacking context</strong>.</p> <p>This can lead to some bizarre situations in which an element with a higher number z-index is not always necessarily “above” an element with a lower z-index.</p> <p>Stacking context can be explained by the following rules.</p> <h3 id="1-the-default-stacking-context-is-the-root-element" tabindex="-1">1. The default stacking context is the root element <a class="header-anchor" href="https://bitsofco.de/how-z-index-works/">#</a></h3> <p>The default stacking context for any HTML document is the root <code>&lt;html&gt;</code> element. Therefore, unless new stacking contexts are created, the stacking level of an element is, by default, in relation to every other element on the page.</p> <h3 id="2-establish-a-new-stacking-context-with-the-z-index-property" tabindex="-1">2. Establish a new stacking context with the z-index property <a class="header-anchor" href="https://bitsofco.de/how-z-index-works/">#</a></h3> <p>We establish a new stacking context by setting the z-index of an element to any integer. This has the effect of, firstly, setting the current element’s stacking level to that integer, and secondly, creating a new stacking context.</p> <p>The new stacking context applies to any children of that element. Their stacking levels are now only within that stacking context, not the root stacking context.</p> <p>In the example below, <code>.foo</code> belongs to stacking context 1, whereas <code>.bar</code> belongs to stacking context 2.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/U5MIwd7ihM-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/U5MIwd7ihM-780.webp 780w"><img alt="Stacking Context" loading="lazy" decoding="async" src="https://bitsofco.de/img/U5MIwd7ihM-780.png" width="780" height="431"></picture></p> <h3 id="3-elements-cannot-be-stacked-above-or-below-the-parent-element-s-stacking-level" tabindex="-1">3. Elements cannot be stacked above (or below) the parent element’s stacking level <a class="header-anchor" href="https://bitsofco.de/how-z-index-works/">#</a></h3> <p>When the parent element’s stacking level is set, that means that its children also cannot be stacked above or below that level (in relation to the parent element's stacking context).</p> <p>In the example below, even though <code>.bar</code> has a higher z-index than <code>.baz</code>, it still shows &quot;below&quot; it. This is because, in stacking context 1, <code>.bar</code> cannot go above or below stacking level 1.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.foo</span> <span class="token punctuation">{</span> <span class="token property">z-index</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.bar</span> <span class="token punctuation">{</span> <span class="token property">z-index</span><span class="token punctuation">:</span> 1000<span class="token punctuation">;</span> <span class="token punctuation">}</span><br><span class="token selector">.baz</span> <span class="token punctuation">{</span> <span class="token property">z-index</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span> <span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/t6dm0cUbqD-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/t6dm0cUbqD-780.webp 780w"><img alt="Stacking Context" loading="lazy" decoding="async" src="https://bitsofco.de/img/t6dm0cUbqD-780.png" width="780" height="481"></picture></p> HTML For Screen Readers - Labelling Elements 2015-10-13T00:00:00Z https://bitsofco.de/html-for-screen-readers-labelling-elements/ <p>To screen readers, a lot of the visual information that is presented on a webpage is lost. Because of this, we need to specifically provide information to them that may be obvious to a person looking at the page.</p> <p>One common way people define information specifically for screen readers is to wrap the descriptive text in an element with a particular class, such as <code>.screen-reader-text</code>, and <a href="https://bitsofco.de/hiding-elements-with-css">hide the element using a method that keeps it visible to screen readers</a>.</p> <p>Although this does work, we can use ARIA attributes that are specifically for this purpose. There are three attributes we can use to provide descriptive text to screen readers -</p> <ul> <li><code>aria-labelledby</code></li> <li><code>aria-label</code></li> <li><code>aria-describedby</code></li> </ul> <h2 id="the-attributes" tabindex="-1">The Attributes <a class="header-anchor" href="https://bitsofco.de/html-for-screen-readers-labelling-elements/">#</a></h2> <h3 id="aria-labelledby" tabindex="-1">aria-labelledby <a class="header-anchor" href="https://bitsofco.de/html-for-screen-readers-labelling-elements/">#</a></h3> <p>This attribute is used to specify the name or label of the current element. User agents use it to generate the &quot;Accessible name&quot;, a plain text name, for the element.</p> <p>To use the attribute, on the element that needs to be labelled, we write the ID of the element that contains the label.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</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>[ID of labelling element]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p>We can specify multiple label elements as well.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</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>element1 element2 element3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p>This attribute should only be used when the labelling element <strong>is visible</strong> on the screen.</p> <h3 id="aria-label" tabindex="-1">aria-label <a class="header-anchor" href="https://bitsofco.de/html-for-screen-readers-labelling-elements/">#</a></h3> <p>This attribute is also used to specify the name of the current element and generate its &quot;Accessible name&quot;. However, it is designed to be used where the label <strong>is not visible</strong> as another element on screen.</p> <p>To use the attribute, instead of referenceing another element, we put the string of text directly.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>[Label text]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <h3 id="aria-describedby" tabindex="-1">aria-describedby <a class="header-anchor" href="https://bitsofco.de/html-for-screen-readers-labelling-elements/">#</a></h3> <p>This attribute is used to specify more descriptive information about the current element. Unlike <code>aria-labelledby</code>, which expects a concisce name for the element, <code>aria-describedby</code> allows for a whole sentence or paragraph to give context to the element.</p> <p>Similar to <code>aria-labelledby</code>, we use the attribute by passing the ID of the element that contains the description.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</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>[ID of element that contains the description]<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p>We can also specify multiple elements if necessary.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</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>element1 element2 element3<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p>The descriptive elements do not necessarily have to be visible on screen.</p> <h2 id="using-the-attributes" tabindex="-1">Using the Attributes <a class="header-anchor" href="https://bitsofco.de/html-for-screen-readers-labelling-elements/">#</a></h2> <p>Here are some examples of how these attributes can be used.</p> <h3 id="provide-context-for-continue-reading-links" tabindex="-1">Provide Context For &quot;Continue Reading&quot; Links <a class="header-anchor" href="https://bitsofco.de/html-for-screen-readers-labelling-elements/">#</a></h3> <p>On the homepage of this blog, for example, after the excert of each blog post there is a link with the text &quot;Continue reading&quot;. To a screen reader, there is no context for where this link will take you. We can use <code>aria-describedby</code> to reference the title of the excerpt and provide context for the link.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>article</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>post-excerpt<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">&lt;</span>h2</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>postID123<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Post Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</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>postID123<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>https://example.com/blog/123<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Continue reading<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span></code></pre> <h3 id="specify-key-information-about-a-widget" tabindex="-1">Specify Key Information About a Widget <a class="header-anchor" href="https://bitsofco.de/html-for-screen-readers-labelling-elements/">#</a></h3> <p>We can specify where the key descriptive information about a widget is. We can use <code>aria-labelledby</code> to define the title of the widget and <code>aria-describedby</code> to define the description.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</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>widget<span class="token punctuation">"</span></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>widgetTitle<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>widgetDesc<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">&lt;</span>h2</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>widgetTitle<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Widget Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</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>widgetDesc<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Quos quidem tibi studiose et diligenter tractandos magnopere censeo<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span> Quae cum praeponunt, ut sit aliqua rerum selectio, naturam videntur sequi<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span></code></pre> <h3 id="additional-instructions-for-form-fields" tabindex="-1">Additional Instructions for Form Fields <a class="header-anchor" href="https://bitsofco.de/html-for-screen-readers-labelling-elements/">#</a></h3> <p>Another use for <code>aria-describedby</code> is to provide additional instructions for form fields. Because the label is generally concise, it can be useful to have more descripive instructions additionally.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</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>passLabel<span class="token punctuation">"</span></span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pass<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Password<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</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>passInstructions<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Your password needs to be at least 8 characters long and have 1 number<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</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>pass<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>password<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>passInstructions<span class="token punctuation">"</span></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>passLabel<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <h3 id="provide-alternative-titles" tabindex="-1">Provide Alternative Titles <a class="header-anchor" href="https://bitsofco.de/html-for-screen-readers-labelling-elements/">#</a></h3> <p>In some cases, the title or label for an element may not be in an acceesible text format. We can use <code>aria-label</code> to provide this alternative in these cases.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Close<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>X<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></code></pre> Using Heading Elements to Create a Document Outline 2015-10-06T00:00:00Z https://bitsofco.de/using-heading-elements-to-create-a-document-outline/ <p>The outline for an HTML document shows the structure of the content on the page. This is useful for user agents, who can use the outline to create, for example, a table of contents for the document. This can then be used by screen readers to help people better navigate the page.</p> <p>The following markup, for example, will produce the following outline -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Using Heading Elements to Create a Document Outline<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Use Each Heading Element to Define a New “Section"<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Use the Heading Ranks to Specify Subsections<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Use the <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span> Element to Explicitly Define Section Barriers<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span></code></pre> <pre><code>1. Using Heading Elements to Create a Document Outline 1. Use Each Heading Element to Define a New &quot;Section&quot;   2. Use the Heading Ranks to Specify Subsections   3. Use the &lt;section&gt; Element to Explicitly Define Section Barriers </code></pre> <p>Creating these outlines is the function of the heading elements - <code>h1</code> ... <code>h6</code> . They provide the title or “theme” for each section of the page, which can then be collated to create the outline.</p> <p>In order to create an accurate and useful outline, we need to use the heading elements in a particular way.</p> <h2 id="use-each-heading-to-define-a-new-section" tabindex="-1">Use Each Heading to Define a New &quot;Section&quot; <a class="header-anchor" href="https://bitsofco.de/using-heading-elements-to-create-a-document-outline/">#</a></h2> <p><strong>The heading elements should only be used to specify the start and title of a new section of content</strong>. Heading elements are sometimes commonly (and wrongly) used to markup subheading or subtitles. They should in fact only be used to define the title of the following section of content.</p> <p>If we imagine the end goal for the document outline - to create a table of contents - we see why this should be the case. If we want to define a subheading, there are other ways to do so that do not interfere with the document outline.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Page Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Subtitle<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span></code></pre> <p>Incorrect method</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Page Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>subheading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Subtitle<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span></code></pre> <p>Correct method</p> <h2 id="use-the-heading-ranks-to-specify-subsections" tabindex="-1">Use the Heading Ranks to Specify Subsections <a class="header-anchor" href="https://bitsofco.de/using-heading-elements-to-create-a-document-outline/">#</a></h2> <p>There are six levels (or “ranks”) of headings - <code>h1</code>, <code>h2</code>, <code>h3</code>, <code>h4</code>, <code>h5</code>, and <code>h6</code>. <strong>The rank for each of the headings should be used to define subsections within broader sections</strong>.</p> <p>Headings of the same level imply equal rank on the page. For example, this page will produce the following outline -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Apples are fruit.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Varieties<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>There are a number of different varieties of Apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">></span></span>Braeburn<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>About these type of apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">></span></span>Royal Gala<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>About these type of apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">></span></span>Pink Lady<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>About these type of apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Taste<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>They tast lovely.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Colour<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Apples come in a variety of colours<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></code></pre> <pre><code>1. Apples 1. Varieties 1. Braeburn 2. Royal Gala 3. Pink Lady 2. Taste 3. Colour </code></pre> <h2 id="use-the-section-element-to-explicitly-define-section-barriers" tabindex="-1">Use the <code>&lt;section&gt;</code> Element to Explicitly Define Section Barriers <a class="header-anchor" href="https://bitsofco.de/using-heading-elements-to-create-a-document-outline/">#</a></h2> <p>In HTML5, a new way of creating an outline was introduced. It was introduced that nesting <code>&lt;section&gt;</code> elements and using only <code>h1</code> could be used to define subsections, instead of using the different heading ranks.</p> <p>For example, to create the same outline from the Apple document above, we can instead write -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Apples are fruit.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Varieties<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>There are a number of different varieties of Apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Braeburn<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>About these type of apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Royal Gala<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>About these type of apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Pink Lady<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>About these type of apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Taste<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>They tast lovely.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Colour<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Apples come in a variety of colours<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span></code></pre> <p>Not currently recognised by user agents</p> <p>This alternative method more easily allows us to change the nest levels without it having an effect on the rest of the document. <strong>However, this method is currently not recognised by any browsers or assitive technology user agents.</strong></p> <p>Using the above <code>&lt;section&gt;</code>-based document structure, most browsers will generate the following outline -</p> <pre><code>1. Apples 2. Varieties 3. Braeburn 4. Royal Gala 5. Pink Lady 6. Taste 7. Colour </code></pre> <p><strong>Therefore, it is advised that we still use the appropriate heading rank</strong>, but in combination with the <code>&lt;section&gt;</code> element for future-proofing. Using the same example, we should instead write -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Apples are fruit.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Varieties<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>There are a number of different varieties of Apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">></span></span>Braeburn<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>About these type of apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">></span></span>Royal Gala<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>About these type of apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">></span></span>Pink Lady<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>About these type of apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Taste<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>They tast lovely.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Colour<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Apples come in a variety of colours<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span></code></pre> <p>Current best practice</p> Sectioning Content in HTML5 - div or section or article? 2015-09-29T00:00:00Z https://bitsofco.de/sectioning-content-in-html5/ <p>HTML5 was a major stepping stone for the concept of semantic code. It really championed the idea that how your document is structured and what tags you use should convey meaning to user agents.</p> <p>Among others, the <code>&lt;section&gt;</code> and <code>&lt;article&gt;</code> elements were introduced as a way to section your content in a more meaningful way than a regular <code>&lt;div&gt;</code>. But when should we use these new elements, and when is a regular <code>&lt;div&gt;</code> preferrable?</p> <h2 id="overview-of-the-elements" tabindex="-1">Overview of The Elements <a class="header-anchor" href="https://bitsofco.de/sectioning-content-in-html5/">#</a></h2> <h3 id="div" tabindex="-1">div <a class="header-anchor" href="https://bitsofco.de/sectioning-content-in-html5/">#</a></h3> <p>The <code>&lt;div&gt;</code> element is the most general purpose element. It has no special meaning. It’s purpose is to group content that is not semantically related. Because it is essentially meaningless to screen readers, it should be sparingly used.</p> <blockquote> <p>Authors are strongly encouraged to view the div element as an element of last resort, for when no other element is suitable.</p> </blockquote> <p><cite><a href="https://www.w3.org/TR/html5/grouping-content.html#the-div-element">W3C Recommendation</a></cite></p> <p>The <code>&lt;div&gt;</code> element, therefore, is mainly used to group content for targeting with CSS. For example, as a styling container for other elements.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>modal-container<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">&lt;</span>section</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>modal<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">&lt;</span>h1</span><span class="token punctuation">></span></span>Modal Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Text goes here<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <h3 id="section" tabindex="-1">section <a class="header-anchor" href="https://bitsofco.de/sectioning-content-in-html5/">#</a></h3> <p>The <code>&lt;section&gt;</code> element is slightly more specific that a <code>&lt;div&gt;</code>. It is applied to a generic section of content that <em>can</em> be grouped together in a semantically meaningful way.</p> <blockquote> <p>A general rule of thumb is that the section element is appropriate only if the element’s contents would be listed explicitly in the document’s outline.</p> </blockquote> <p><cite><a href="https://www.w3.org/TR/html5/sections.html#the-section-element">W3C Recommendation</a></cite></p> <p>Because the contents of a <code>&lt;section&gt;</code> are meaningful when grouped together, they should have a “theme”. A <code>&lt;section&gt;</code>’s “theme” should be defined by including a heading element within the element, often immediately after the opening tag.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</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>newsletter<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">&lt;</span>h1</span><span class="token punctuation">></span></span>Subscribe to the Newsletter<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- ... --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span></code></pre> <h3 id="article" tabindex="-1">article <a class="header-anchor" href="https://bitsofco.de/sectioning-content-in-html5/">#</a></h3> <p>The <code>&lt;article&gt;</code> element is even more specific than a <code>&lt;section&gt;</code>. It is also applied to a section of content that is semantically related, and should also have a heading. However, this section of content should additionally be &quot;self-contained&quot;. This means that the contents of an <code>&lt;article&gt;</code> should be able to be isolated from the rest of the page and still be meaningful.</p> <p>One common purpose for an <code>&lt;article&gt;</code> is in marking content for syndication. For example, marking a blog post.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>article</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>post<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">&lt;</span>h1</span><span class="token punctuation">></span></span>Article Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet, consectetur adipiscing elit...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Quae similitudo in genere etiam humano apparet. Est, ut dicis, inquam...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span></code></pre> <h2 id="div-or-section-or-article" tabindex="-1">div or section or article? <a class="header-anchor" href="https://bitsofco.de/sectioning-content-in-html5/">#</a></h2> <p>So which should you use and when?</p> <p>If the content within the element is not <strong>semantically related</strong>, then use a <code>&lt;div&gt;</code>. If the semantically related content is also able to be <strong>self-contained</strong>, then use an <code>&lt;article&gt;</code>. Otherwise, use a <code>&lt;section&gt;</code>.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/wreOe6bF5Y-1169.avif 1169w"><source type="image/webp" srcset="https://bitsofco.de/img/wreOe6bF5Y-1169.webp 1169w"><img alt="Flowchart - Div or Section or Article" loading="lazy" decoding="async" src="https://bitsofco.de/img/wreOe6bF5Y-1169.jpeg" width="1169" height="743"></picture></p> <h2 id="combining-the-elements" tabindex="-1">Combining the Elements <a class="header-anchor" href="https://bitsofco.de/sectioning-content-in-html5/">#</a></h2> <p>Where things can get a bit complicated is when we try to combine the different elements together.</p> <h3 id="articles-within-an-article" tabindex="-1">Articles within an Article <a class="header-anchor" href="https://bitsofco.de/sectioning-content-in-html5/">#</a></h3> <p>We can nest <code>&lt;article&gt;</code> elements within each other. Although still self-contained, inner <code>&lt;article&gt;</code>s are assumed to be related to the contents of the outer <code>&lt;article&gt;</code>.</p> <p>For example, if there is a long excerpt within a a blog post that is written by another person, it could be contained in its own <code>&lt;article&gt;</code>.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>article</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Article Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>author<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>John Smith<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet, consectetur adipiscing elit.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>article</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Another Article<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>author<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Jane Doe<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet, consectetur adipiscing elit.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet, consectetur adipiscing elit.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span></code></pre> <h3 id="articles-within-a-section" tabindex="-1">Articles within a Section <a class="header-anchor" href="https://bitsofco.de/sectioning-content-in-html5/">#</a></h3> <p>We can also have multiple <code>&lt;article&gt;</code> elements within a <code>&lt;section&gt;</code> element. A good example of this is a blog homepage showing the latest posts. The container for all the latest posts would be a <code>&lt;section&gt;</code>, whereas each individual post excerpt could be an <code>&lt;article&gt;</code>.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Latest Blog Posts<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>article</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Blog Post Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>article</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Blog Post Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet, consectetur adipiscing elit. <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span></code></pre> <h3 id="sections-within-an-article" tabindex="-1">Sections within an Article <a class="header-anchor" href="https://bitsofco.de/sectioning-content-in-html5/">#</a></h3> <p>Each individual <code>&lt;article&gt;</code> may also have <code>&lt;section&gt;</code> elements within it. For example, this particular post could be written 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">&lt;</span>article</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Sectioning Content in HTML5 - div or section or article?<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Overview of the Elements<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">></span></span>div<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>The div element is the most general purpose element.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">></span></span>section<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>The section element is slightly more specific that a div<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">></span></span>article<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>The article element is even more specific than a section<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>div or section or article?<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token comment">&lt;!-- ... --></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Combining the Elements<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token comment">&lt;!-- ... --></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">></span></span></code></pre> The Style Element 2015-09-15T00:00:00Z https://bitsofco.de/the-style-element/ <p>The <code>&lt;style&gt;</code> element is a &quot;metadata” type element, which means its purpose is to provide setup for how the rest of the document will be displayed. We use this element to embed style declarations within our HTML document, rather than linking to an external dedicated stylesheet. It is essentially a form of inline styling.</p> <p>In addition to the global HTML attributes, the style element accepts the following attributes -</p> <table> <thead> <tr> <th>Attribute</th> <th>Description</th> <th>Default</th> </tr> </thead> <tbody> <tr> <td><code>type</code></td> <td>A valid MIME type that specifies the language for the style</td> <td><code>”text/css”</code></td> </tr> <tr> <td><code>media</code></td> <td>A valid media query</td> <td><code>”all”</code></td> </tr> <tr> <td><code>title</code></td> <td>Specifies a title for an alternate style element</td> <td>none</td> </tr> <tr> <td><code>scoped</code>*</td> <td>Allows us to place a <code>&lt;style&gt;</code> element within the body and limit the scope of the styles to the parent element</td> <td>none/false</td> </tr> </tbody> </table> <p>* <a href="https://caniuse.com/#feat=style-scoped">Limited browser support</a></p> <p>By default, most browsers apply a single line of styling to the <code>&lt;style&gt;</code> element -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">style</span> <span class="token punctuation">{</span><br> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Here are some things you may not know you can do with the style element.</p> <h2 id="styling-the-style-element" tabindex="-1">Styling the <code>style</code> element <a class="header-anchor" href="https://bitsofco.de/the-style-element/">#</a></h2> <p>Although it may not always seem like it, the <code>&lt;style&gt;</code> element is just like any other element in an HTML document. This means that it can be targeted with CSS and even styled.</p> <p>Not only can we override the default styling and show the content of the <code>&lt;style&gt;</code> element, we can add any style we could to a regular <code>div</code>.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">style</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 property">background-color</span><span class="token punctuation">:</span> palevioletred<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> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">html</span> <span class="token punctuation">{</span><br> <span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">'Proxima Nova'</span><span class="token punctuation">;</span><br> <span class="token property">padding</span><span class="token punctuation">:</span> 30px<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><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/iuVbsNjnm_-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/iuVbsNjnm_-780.webp 780w"><img alt="Styleception" loading="lazy" decoding="async" src="https://bitsofco.de/img/iuVbsNjnm_-780.png" width="780" height="155"></picture></p> <p>Similarly, we can use the <code>style</code> attribute to the <code>&lt;style&gt;</code> element and apply CSS to it directly within the HTML.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</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 value css language-css"><span class="token property">“display</span><span class="token punctuation">:</span></span></span></span> <span class="token attr-name">block;</span> <span class="token attr-name"><span class="token namespace">background-color:</span></span> <span class="token attr-name">palevioletred;</span> <span class="token attr-name"><span class="token namespace">color:</span></span> <span class="token attr-name">#fff;"</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> // styles<br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span></code></pre> <p>We can even target the <code>&lt;style&gt;</code> element within itself -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">style</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 property">background-color</span><span class="token punctuation">:</span> palevioletred<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><br></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>style</span><span class="token punctuation">></span></span></code></pre> <h2 id="placement-of-the-style-element" tabindex="-1">Placement of the <code>&lt;style&gt;</code> element <a class="header-anchor" href="https://bitsofco.de/the-style-element/">#</a></h2> <p>The <code>&lt;style&gt;</code> element does not necessarily have to be within the <code>&lt;head&gt;</code> (although that is currently the best pratice for HTML validation).</p> <p>HTML5 introduced the <code>scoped</code> attribute, which is supposed to allow us to insert the <code>&lt;style&gt;</code> attribute anywhere within the <code>body</code> and limit the scope of the styles to the element’s immediate parent. For example -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>This sentence colour should be the default black<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span> <span class="token attr-name">scoped</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">p</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> plum<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">&lt;/</span>style</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>This sentence colour should be plum<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br></code></pre> <p>Unfortunately, the browser support for this attribute is very low at the moment. Currently, <a href="https://caniuse.com/#feat=style-scoped">only Firefox supports it</a> out of the box.</p> <p>However, <strong>most browsers will still allow us to place the</strong> <code>&lt;style&gt;</code> <strong>element within the</strong> <code>&lt;body&gt;</code>, but without the scoping ability. The styles will just apply to the whole document. So, with the example HTML above, both <code>&lt;p&gt;</code>s will appear in plum for most major browsers.</p> <h2 id="conditional-styling-with-noscript" tabindex="-1">Conditional styling with <code>noscript</code> <a class="header-anchor" href="https://bitsofco.de/the-style-element/">#</a></h2> <p>Besides being able to conditionally apply styles with the <code>media</code> attribute, we can specify different styles to apply depending on if javascript is enabled in the browser.</p> <p>We do this by placing the conditional styling within a <code>&lt;style&gt;</code> element within a <code>&lt;noscript&gt;</code> element in the <code>&lt;head&gt;</code>. For example -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span><br> <span class="token comment">&lt;!-- Default styling --></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">p</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> plum<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">&lt;/</span>style</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>noscript</span><span class="token punctuation">></span></span><br> <span class="token comment">&lt;!-- Styling for when javascript is disabled --></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">p</span> <span class="token punctuation">{</span> <span class="token property">color</span><span class="token punctuation">:</span> cornflowerblue<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">&lt;/</span>style</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>noscript</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>What colour will this sentence be?<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/_PdeD-Eg7J-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/_PdeD-Eg7J-780.webp 780w"><img alt="Style Applied With Javascript Enabled" loading="lazy" decoding="async" src="https://bitsofco.de/img/_PdeD-Eg7J-780.png" width="780" height="131"></picture> JavaScript Enabled</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/kss7i1-sqx-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/kss7i1-sqx-780.webp 780w"><img alt="Style Applied With Javascript Disabled" loading="lazy" decoding="async" src="https://bitsofco.de/img/kss7i1-sqx-780.png" width="780" height="124"></picture> JavaScript Disabled</p> InstaChrome - A Google Chrome New Tab Extension 2015-09-08T00:00:00Z https://bitsofco.de/instachrome/ <p>Last week, I made a small extension for the Google Chrome browser to replace the default “New Tab” page. I had previously tried extensions like <a href="https://usepanda.com">Panda</a>, but found them too distracting, and wanted something that would be nice to look at, but not too attention-grabbing.</p> <p><a href="https://chrome.google.com/webstore/detail/instachrome/cdkpcfbopbdakdkfbpeaicfalbbehfnn">InstaChrome</a> is an extension for Google Chrome that allows you display the latest 6 instagram posts from a tag of your choice.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Oaj5xb9Bjt-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/Oaj5xb9Bjt-780.webp 780w"><img alt="InstaChrome Screenshot" loading="lazy" decoding="async" src="https://bitsofco.de/img/Oaj5xb9Bjt-780.png" width="780" height="517"></picture></p> <p><a href="https://chrome.google.com/webstore/detail/instachrome/cdkpcfbopbdakdkfbpeaicfalbbehfnn">Download from the Google Chrome Store</a></p> <p>With the exception of a few things, making this was like making a regular javascript application. As always, you can view the full app on <a href="https://github.com/ireade/InstaChrome">my github</a>. But here is an overview of how I made the extension.</p> <h2 id="making-a-chrome-extension" tabindex="-1">Making a Chrome Extension <a class="header-anchor" href="https://bitsofco.de/instachrome/">#</a></h2> <p>There were two key things that this extension different from a regular web app - the <code>manifest.json</code> file, and storing data in the browser’s local storage.</p> <h3 id="manifest-json" tabindex="-1">manifest.json <a class="header-anchor" href="https://bitsofco.de/instachrome/">#</a></h3> <p>The <code>manifest.json</code> file is a required file that stores all the metadata for a Chrome extension. It is where we declare basic information such as the name and description of the extension, as well as functional information such as the permissions required and any overrides.</p> <p>This was my <code>manifest.json</code> file -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token punctuation">{</span><br> <span class="token comment">// The version of manifest.json we are using</span><br> <span class="token string-property property">"manifest_version"</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span><br><br> <span class="token comment">// Required information about the extension</span><br> <span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"InstaChrome"</span><span class="token punctuation">,</span><br> <span class="token string-property property">"description"</span><span class="token operator">:</span> <span class="token string">"An extension for chrome that displays Instagram posts from a tag of your choice."</span><span class="token punctuation">,</span><br> <span class="token string-property property">"version"</span><span class="token operator">:</span> <span class="token string">"1"</span><span class="token punctuation">,</span><br><br> <span class="token comment">// Overriding the default behaviour for a new tab to redirect to our index.html file</span><br> <span class="token string-property property">"chrome_url_overrides"</span><span class="token operator">:</span><span class="token punctuation">{</span><br> <span class="token string-property property">"newtab"</span> <span class="token operator">:</span> <span class="token string">"index.html"</span><br> <span class="token punctuation">}</span><span class="token punctuation">,</span><br><br> <span class="token comment">// Allow use of chrome local storage</span><br> <span class="token string-property property">"permissions"</span><span class="token operator">:</span> <span class="token punctuation">[</span><br> <span class="token string">"storage"</span><br> <span class="token punctuation">]</span><span class="token punctuation">,</span><br><br> <span class="token comment">// Allow requests from api.instagram.com</span><br> <span class="token string-property property">"content_security_policy"</span><span class="token operator">:</span> <span class="token string">"script-src 'self' 'unsafe-eval' https://api.instagram.com; object-src 'self'"</span><br><span class="token punctuation">}</span></code></pre> <h3 id="working-with-local-storage" tabindex="-1">Working With Local Storage <a class="header-anchor" href="https://bitsofco.de/instachrome/">#</a></h3> <p>With this extension, I wanted to be able to save the last tag searched for in local storage so a user wouldn't need to search again everytime they open a new tab. To do this, we can use the <code>chrome.storage</code> API.</p> <p>To store a variable, we use the <code>set</code> function on the storage area we want, in this case, <code>local</code> -</p> <pre class="language-js" tabindex="0"><code class="language-js">chrome<span class="token punctuation">.</span>storage<span class="token punctuation">.</span>local<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">label</span> <span class="token operator">:</span> value<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">function</span><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><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>To retrieve data based on a given label, we use the <code>get</code> function -</p> <pre class="language-js" tabindex="0"><code class="language-js">chrome<span class="token punctuation">.</span>storage<span class="token punctuation">.</span>local<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>label<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">result</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><span class="token punctuation">)</span></code></pre> <p>To remove data from storage, we use the <code>remove</code> function -</p> <pre class="language-js" tabindex="0"><code class="language-js">chrome<span class="token punctuation">.</span>storage<span class="token punctuation">.</span>local<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>label<span class="token punctuation">,</span> <span class="token keyword">function</span><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><span class="token punctuation">)</span></code></pre> <h2 id="instachrome" tabindex="-1">InstaChrome <a class="header-anchor" href="https://bitsofco.de/instachrome/">#</a></h2> <p>Having set all that up, this was my main app file -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token string">"use strict"</span><span class="token punctuation">;</span><br><span class="token function">$</span><span class="token punctuation">(</span>document<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ready</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">var</span> <span class="token function-variable function">request</span> <span class="token operator">=</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> chrome<span class="token punctuation">.</span>storage<span class="token punctuation">.</span>local<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"tag"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">result</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">var</span> tag <span class="token operator">=</span> result<span class="token punctuation">.</span>tag<span class="token punctuation">;</span><br> <span class="token keyword">var</span> cleanTag<span class="token punctuation">;</span><br><br> <span class="token comment">// Display # on the front end, but remove # for the cleanTag variable</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> tag<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span><span class="token string">"#"</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token operator">-</span><span class="token number">1</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> cleanTag <span class="token operator">=</span> tag<span class="token punctuation">;</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'input[type="text"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">val</span><span class="token punctuation">(</span><span class="token string">'#'</span><span class="token operator">+</span>tag<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> cleanTag <span class="token operator">=</span> tag<span class="token punctuation">.</span><span class="token function">split</span><span class="token punctuation">(</span><span class="token string">'#'</span><span class="token punctuation">)</span><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 punctuation">}</span><br><br> cleanTag <span class="token operator">=</span> cleanTag<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token regex"><span class="token regex-delimiter">/</span><span class="token regex-source language-regex">\s</span><span class="token regex-delimiter">/</span><span class="token regex-flags">g</span></span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">var</span> requestUrl <span class="token operator">=</span> <span class="token string">'https://api.instagram.com/v1/tags/'</span><span class="token operator">+</span>cleanTag<span class="token operator">+</span><span class="token string">'/media/recent?access_token='</span><span class="token operator">+</span>accessToken<span class="token operator">+</span><span class="token string">'&amp;callback=?'</span><span class="token punctuation">;</span><br><br> $<span class="token punctuation">.</span><span class="token function">getJSON</span><span class="token punctuation">(</span>requestUrl<span class="token punctuation">,</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">var</span> items <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">// Get only 6 items</span><br> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">var</span> i<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">6</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> items<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span>data<span class="token punctuation">[</span>i<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 comment">// Setup Handlebars templating</span><br> <span class="token keyword">var</span> source <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'#grams-template'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">html</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">var</span> template <span class="token operator">=</span> Handlebars<span class="token punctuation">.</span><span class="token function">compile</span><span class="token punctuation">(</span>source<span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token keyword">var</span> output <span class="token operator">=</span> <span class="token function">template</span><span class="token punctuation">(</span> <span class="token punctuation">{</span><span class="token literal-property property">Grams</span><span class="token operator">:</span> items<span class="token punctuation">}</span> <span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.loading'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">hide</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span>data<span class="token punctuation">.</span>data<span class="token punctuation">.</span>length <span class="token operator">===</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token comment">// Show error messages if no images for this tag</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.message-error'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</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 keyword">else</span> <span class="token punctuation">{</span><br> <span class="token comment">// Else, display the images in the template</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">"#grams"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">html</span><span class="token punctuation">(</span>output<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">fadeIn</span><span class="token punctuation">(</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><br><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// end get local storage</span><br> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token comment">// end request</span><br><br> <span class="token comment">// When users search</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'#search'</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">'submit'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">// Clear messages and any previous images, and show loading animation</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.message'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">hide</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">"#grams"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">html</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.loading'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token keyword">var</span> searchInput <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'#searchText'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">val</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Set the search text to local storage</span><br> chrome<span class="token punctuation">.</span>storage<span class="token punctuation">.</span>local<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token punctuation">{</span><span class="token string-property property">"tag"</span> <span class="token operator">:</span> searchInput<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><br> <span class="token function">request</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 keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br><br> <span class="token comment">// On load of the new tab, check if there is any tag previously stored</span><br> chrome<span class="token punctuation">.</span>storage<span class="token punctuation">.</span>local<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"tag"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">result</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> result<span class="token punctuation">.</span>tag <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token function">request</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'input[type="text"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">val</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span>tag<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 function">$</span><span class="token punctuation">(</span><span class="token string">'.loading'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">hide</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.message-welcome'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">show</span><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 punctuation">}</span><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>You can check out the extension on <a href="https://github.com/ireade/InstaChrome">github</a> or <a href="https://chrome.google.com/webstore/detail/instachrome/cdkpcfbopbdakdkfbpeaicfalbbehfnn">download it from the Chrome store</a>. If you use this extension, or have any feedback on how I can improve on it, do leave a comment below.</p> Alternatives to Floating 2015-09-01T00:00:00Z https://bitsofco.de/alternatives-to-floating/ <p>A few weeks ago, I wrote about <a href="https://bitsofco.de/how-floating-works">how floating works</a>. I also mentioned that I rarely use floats myself, and got a few questions about why this is and what I use as an alternative.</p> <p>The primary reason I tend to avoid floatings is because <strong>floated elements don't always work well with sibling elements that are not also floated</strong>. For example, this simple scenario -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>container<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">&lt;</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>logo<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Logo<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>right<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Element floated right<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 750px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.logo</span> <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 punctuation">}</span><br> <span class="token selector">.right</span> <span class="token punctuation">{</span><br> <span class="token property">float</span><span class="token punctuation">:</span> right<span class="token punctuation">;</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><br><br><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 750px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.logo</span> <span class="token punctuation">{</span><br> <span class="token property">margin</span><span class="token punctuation">:</span> 0 auto<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> <span class="token selector">.right</span> <span class="token punctuation">{</span><br> <span class="token property">float</span><span class="token punctuation">:</span> none<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 property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/rWeien5LWq-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/rWeien5LWq-720.gif 720w"><img alt="The problem with Floating" loading="lazy" decoding="async" src="https://bitsofco.de/img/rWeien5LWq-720.webp" width="720" height="443"></picture></p> <p>This happens because <a href="https://bitsofco.de/how-floating-works">previously defined full-width elements push a floated element down</a></p> <p>Although <strong>there are ways around this still using floats</strong> - for example also floating the logo element or rearranging the HTML so that the logo is defined after - they are not always suitable and so I tend to use an alternative.</p> <p>The two property/value combinations I typically use as an alternative to floating are -</p> <ul> <li><code>display: inline-block</code></li> <li><code>position: absolute</code></li> </ul> <p>Here are two scenarios in which I use these alternatives (and one where I still use floats).</p> <h2 id="display-inline-block" tabindex="-1">display: inline-block <a class="header-anchor" href="https://bitsofco.de/alternatives-to-floating/">#</a></h2> <p>One of the popular uses for floats is in creating layouts. This is because you can quite easily have items perfectly aligned and span the full width of the parent element. For example -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/OHu7g4nFxi-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/OHu7g4nFxi-780.webp 780w"><img alt="Creating Layouts" loading="lazy" decoding="async" src="https://bitsofco.de/img/OHu7g4nFxi-780.png" width="780" height="171"></picture></p> <p>An alternative we can use to create this same effect is <code>display: inline-block</code>.</p> <p>However, this alternative is not without its own issues. When an element is inline, extra spacing (about 3px) is added to the right of it. There are a few ways to get around this, and there is a great article outlining some of them on <a href="https://css-tricks.com/fighting-the-space-between-inline-block-elements/">CSS tricks</a>.</p> <p><strong>The solution I frequently use is to remove any whitespace in the HTML between the elements</strong>. This can be acheived using HTML comments. For example -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>container<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">&lt;</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>box<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> width: 50%;<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token comment">&lt;!--<br> --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>box<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> width: 25%;<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><span class="token comment">&lt;!--<br> --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>box<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> width: 25%;<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.box</span> <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 punctuation">}</span></code></pre> <p>Although this is definitely not a perfect solution, depending on the situation, it can be preferable to floating.</p> <h2 id="position-absolute" tabindex="-1">Position: Absolute <a class="header-anchor" href="https://bitsofco.de/alternatives-to-floating/">#</a></h2> <p>Another scenario in which floats are used is when we want to align sibling elements on the same line both left and right. Because we want elements within the same parent to be aligned in opposite ways, we cannot simply use <code>text-align: left</code> or <code>right</code> on the parent element.</p> <p>A typical example for this is a website header, in which we have the logo aligned to the left and the navigation aligned to the right.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/4yJh1r2vuD-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/4yJh1r2vuD-780.webp 780w"><img alt="Floating Images With Text Wrap" loading="lazy" decoding="async" src="https://bitsofco.de/img/4yJh1r2vuD-780.png" width="780" height="130"></picture></p> <p>For this scenario, we can use <code>position: absolute</code> on one of the elements to align it in the other direction. For example -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 750px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.logo</span> <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 punctuation">}</span><br> <span class="token selector">.right</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">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span><br><br><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">max-width</span><span class="token punctuation">:</span> 750px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">.logo</span> <span class="token punctuation">{</span><br> <span class="token property">margin</span><span class="token punctuation">:</span> 0 auto<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> <span class="token selector">.right</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> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Cgf0U8T5Pm-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/Cgf0U8T5Pm-720.gif 720w"><img alt="Using position absolute as an alternative" loading="lazy" decoding="async" src="https://bitsofco.de/img/Cgf0U8T5Pm-720.webp" width="720" height="457"></picture></p> <h2 id="a-case-for-floating" tabindex="-1">A Case For Floating <a class="header-anchor" href="https://bitsofco.de/alternatives-to-floating/">#</a></h2> <p>Although these alternatives can cover most cases, <strong>there are still situations in which just sticking to floating can be better</strong>. The most common situation is when we want to have en element, typically an image, aligned left/right with text wrapping around. For example -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Xd3sRui5vh-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/Xd3sRui5vh-780.webp 780w"><img alt="Floating Images With Text Wrap" loading="lazy" decoding="async" src="https://bitsofco.de/img/Xd3sRui5vh-780.png" width="780" height="261"></picture></p> <p>As far as I know, there is no simpler or better way to create this effect without floating. But also, an alternative isn't always necessary.</p> Custom, Scriptless, and Trackable Social Media Share Buttons 2015-08-25T00:00:00Z https://bitsofco.de/custom-scriptless-trackable-social-media-share-buttons/ <p>For a freelance project I am currently working on, the client wanted more advanced insight into where their traffic was coming from and, more specifically, if people were arriving from links shared via the social media share buttons on the website.</p> <p>To do this, I created some custom social media share buttons that were lightweight, scriptless, and included tracking information for google analytics.</p> <p>These were the final buttons -</p> <ul> <li><a href="https://www.facebook.com/sharer/sharer.php?u=https://bitsofco.de?utm_source=facebook%26utm_medium=social%26utm_campaign=share_buttons">Share on Facebook</a></li> <li><a href="https://twitter.com/intent/tweet/?text=bitsofcode&amp;url=https://bitsofco.de?utm_source=twitter%26utm_medium=social%26utm_campaign=share_buttons&amp;via=ireaderinokun">Share on Twitter</a></li> <li><a href="https://plus.google.com/share?url=https://bitsofco.de?utm_source=googleplus%26utm_medium=social%26utm_campaign=share_buttons">Share on Google Plus</a></li> <li>[Share on LinkedIn](https://www.linkedin.com/shareArticle ?mini=true%26title%26source=bitsofcode%26url=https://bitsofco.de?utm_source=googleplus%26utm_medium=social%26utm_campaign=share_buttons)</li> <li><a href="https://www.pinterest.com/pin/create/button/?url=https://bitsofco.de?utm_source=pinterest%26utm_medium=social%26utm_campaign=share_buttons&amp;description=bitsofcode">Share on Pinterest</a></li> <li><a href="mailto:?subject=bitsofcode&amp;body=https://bitsofco.de?utm_source=email%26utm_medium=email%26utm_campaign=share_buttons">Share via Email</a></li> <li><a href="whatsapp://send?text=bitsofcode-https://bitsofco.de?utm_source=whatsapp%26utm_medium=social%26utm_campaign=share_buttons">Share on Whatsapp</a></li> </ul> <p>You can view the source code for this on <a href="https://github.com/ireade/custom-social-buttons">my github</a> and use it in your own project.</p> <h2 id="scriptless-social-media-buttons" tabindex="-1">Scriptless Social Media Buttons <a class="header-anchor" href="https://bitsofco.de/custom-scriptless-trackable-social-media-share-buttons/">#</a></h2> <p>A typical way of adding social media share buttons to a site would include loading third-party scripts from each of the social media websites. However, most of these sites also provide a scriptless alternative in the form of a special “sharer” URL.</p> <p>With this alternative, all we do is direct users to a specific url, adding the parameters we want to be shared, such as the page title and link. For example, a twitter share url for this blog could 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">&lt;</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/intent/tweet/?text=Reading%20the%20bitsofcode%20blog&amp;url=https://bitsofco.de&amp;via=ireaderinokun<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Share on Twitter<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span></code></pre> <p>When clicked, it will generate this tweet -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/3XL89bjJJe-680.avif 680w"><source type="image/webp" srcset="https://bitsofco.de/img/3XL89bjJJe-680.webp 680w"><img alt="Tweet" loading="lazy" decoding="async" src="https://bitsofco.de/img/3XL89bjJJe-680.png" width="680" height="173"></picture></p> <p>The base url and parameters we can add differ depending on the social media site. For example, with Facebook you can only pass the url you want to share, whereas with Twitter you can pass a lot more information such as hastags and related users.</p> <p>Here are the parameters we can add for some popular social media sites (and email).</p> <table> <thead> <tr> <th>Platform</th> <th>Base Share URL</th> <th>Parameters</th> </tr> </thead> <tbody> <tr> <td>Facebook</td> <td><code>https://www.facebook.com/sharer/sharer.php?</code></td> <td><code>u</code> - url</td> </tr> <tr> <td>Twitter</td> <td><code>https://twitter.com/intent/tweet/?</code></td> <td><code>url``text``hashtags</code> - a comma-separated list (no spaces)<code>via</code> - a twitter username<code>related</code> - suggested related twitter usernames<code>in-reply-to</code> - Tweet ID of tweet to reply to</td> </tr> <tr> <td>Google Plus</td> <td><code>https://plus.google.com/share?</code></td> <td><code>url</code></td> </tr> <tr> <td>LinkedIn</td> <td><code>https://www.linkedin.com/shareArticle?</code></td> <td><code>mini=true</code> - required parameter<code>url``title``summary``source</code> - your website or app name</td> </tr> <tr> <td>Pinterest</td> <td><code>https://www.pinterest.com/pin/create/button/?</code></td> <td><code>url``media</code> - image attachments<code>description``hashtags</code></td> </tr> <tr> <td>Whatsapp</td> <td><code>whatsapp://send?</code></td> <td><code>text</code></td> </tr> <tr> <td>Email</td> <td><code>mailto:?</code></td> <td><code>subject``body</code></td> </tr> </tbody> </table> <h3 id="working-with-dynamic-content" tabindex="-1">Working With Dynamic Content <a class="header-anchor" href="https://bitsofco.de/custom-scriptless-trackable-social-media-share-buttons/">#</a></h3> <p>In most cases, we want the shared content to change depending on what page the user is currently on. For the project I was working on, it was a wordpress blog, and so the url and any text should be different depending on the blog post being shared.</p> <p>To do this, instead of writing out the URL and title, we instead add a marker for the dynamic content. For example, the twitter URL could instead look like this -</p> <pre class="language-php" tabindex="0"><code class="language-php">&lt;a href=“https://twitter.com/intent/tweet/?text=<span class="token php language-php"><span class="token delimiter important">&lt;?php</span> <span class="token function">the_title</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token delimiter important">?></span></span>&amp;url=<span class="token php language-php"><span class="token delimiter important">&lt;?php</span> <span class="token function">the_permalink</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token delimiter important">?></span></span>”><br> Share on Twitter<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span></code></pre> <h3 id="url-encoding" tabindex="-1">URL Encoding <a class="header-anchor" href="https://bitsofco.de/custom-scriptless-trackable-social-media-share-buttons/">#</a></h3> <p>URLs can only be sent using the ASCII character set. This means that certain special characters, such as spaces, hastags and exclamation marks, need to be converted (&quot;encoded&quot;) into a valid format.</p> <p>There are a number of different ways to encode a URL. If you are dealing with static, predetermined text, you can use <a href="https://meyerweb.com/eric/tools/dencoder/">Eric Meyer's URL Decoder/Encoder</a>. Since this was a wordpress blog and I was using PHP, I used the native <code>urlencode</code> function.</p> <pre class="language-php" tabindex="0"><code class="language-php"><span class="token php language-php"><span class="token delimiter important">&lt;?php</span><br> <span class="token comment">// First get the raw details</span><br> <span class="token variable">$rawTitle</span> <span class="token operator">=</span> <span class="token function">get_the_title</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token variable">$rawPermalink</span> <span class="token operator">=</span> <span class="token function">get_the_permalink</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">// Get clean details</span><br> <span class="token variable">$cleanTitle</span> <span class="token operator">=</span> <span class="token function">urlencode</span><span class="token punctuation">(</span><span class="token variable">$rawTitle</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token variable">$cleanPermalink</span> <span class="token operator">=</span> <span class="token function">urlencode</span><span class="token punctuation">(</span><span class="token variable">$rawPrmalink</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token delimiter important">?></span></span><br><br>&lt;a href=“https://twitter.com/intent/tweet/?text=<span class="token php language-php"><span class="token delimiter important">&lt;?php</span> <span class="token keyword">echo</span> <span class="token variable">$cleanTitle</span><span class="token punctuation">;</span> <span class="token delimiter important">?></span></span>&amp;url=<span class="token php language-php"><span class="token delimiter important">&lt;?php</span> <span class="token keyword">echo</span> <span class="token variable">$cleanPermalink</span><span class="token punctuation">;</span> <span class="token delimiter important">?></span></span>”><br> Share on Twitter<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span></code></pre> <h2 id="adding-events-tracking" tabindex="-1">Adding Events Tracking <a class="header-anchor" href="https://bitsofco.de/custom-scriptless-trackable-social-media-share-buttons/">#</a></h2> <p>Once we have the share buttons, we can start adding analytics tracking. After installing the Google Analytics tracking code, the first thing we want to do is track how many people are clicking on the share buttons.</p> <p>To do this, we want to send an instance of the event when a user clicks the button. We acheive this using the Google Analytics <code>ga</code> function with a hit type of <code>event</code>. For example -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'.social-share-btns a'</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">'click'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token comment">/* Expanded syntax */</span><br> <span class="token function">ga</span><span class="token punctuation">(</span><span class="token string">'send'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span><br> <span class="token string-property property">'hitType'</span><span class="token operator">:</span> <span class="token string">'event'</span><span class="token punctuation">,</span> <span class="token comment">// required </span><br> <span class="token string-property property">'eventCategory'</span><span class="token operator">:</span> <span class="token string">'button'</span><span class="token punctuation">,</span> <span class="token comment">// required </span><br> <span class="token string-property property">'eventAction'</span><span class="token operator">:</span> <span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token comment">// required </span><br> <span class="token string-property property">'eventLabel'</span><span class="token operator">:</span> <span class="token string">'social share buttons'</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">/* Shortened syntax */</span><br> <span class="token function">ga</span><span class="token punctuation">(</span><span class="token string">'send'</span><span class="token punctuation">,</span> <span class="token string">'event'</span><span class="token punctuation">,</span> <span class="token string">'button'</span><span class="token punctuation">,</span> <span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token string">'social share buttons'</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></code></pre> <p>The <strong>event category</strong> is the object that the event is tied to, in this case a <em>button</em>. The <strong>event action</strong> is the type of interaction; in this case, the user is <em>clicking</em> on the button. The <strong>event label</strong> is a string we use to categorise the event.</p> <p>Once data has been collected, in the Google Analytics report, the data can be found under <strong>Behaviour &gt; Events &gt; Overview</strong>.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/slmhtlX87H-779.avif 779w"><source type="image/webp" srcset="https://bitsofco.de/img/slmhtlX87H-779.webp 779w"><img alt="Events Google Analytics Report" loading="lazy" decoding="async" src="https://bitsofco.de/img/slmhtlX87H-779.png" width="779" height="417"></picture></p> <h2 id="adding-custom-analytics-campaigns" tabindex="-1">Adding Custom Analytics Campaigns <a class="header-anchor" href="https://bitsofco.de/custom-scriptless-trackable-social-media-share-buttons/">#</a></h2> <p>Once we know how many people are sharing the page, we can measure the effectiveness of those shares by tracking if people are arriving to the site through the links that were shared.</p> <p>Campaigns are a way of categorising and tracking the acquisition of users to the site. We implement a campaign by adding special “UTM&quot; paramters to the URL.</p> <p>There are five of these parameters we can add -</p> <table> <thead> <tr> <th>Parameter</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>utm_campaign</code></td> <td>The name for the campaign</td> </tr> <tr> <td><code>utm_source</code></td> <td>The referring website, e.g. facebook</td> </tr> <tr> <td><code>utm_medium</code></td> <td>The category for the referrer, e.g. social, email, search</td> </tr> <tr> <td><code>utm_content</code></td> <td>A term to differentiate between two links that may have the same campaign, source and medium</td> </tr> <tr> <td><code>utm_term</code></td> <td>Used to identify paid search keywords</td> </tr> </tbody> </table> <p>For this project, I only used the first three parameters.</p> <ul> <li><code>utm_campaign=share_buttons</code></li> <li><code>utm_source=[social media site]</code> , e.g. <code>utm_source=twitter</code></li> <li><code>utm_medium=social</code></li> </ul> <p>This is what the Twitter button looked like -</p> <pre class="language-html" tabindex="0"><code class="language-html">&lt;a href=“https://twitter.com/intent/tweet/?text=<span class="token prolog">&lt;?php echo $cleanTitle; ?></span>&amp;url=<span class="token prolog">&lt;?php echo $cleanPermalink; ?></span>?utm_source=twitter%26utm_medium=social%26utm_campaign=share_buttons”><br> Share on Twitter<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span></code></pre> <p>Because of the text restraints for Twitter, it may be better to restrict the utm paramters to only the <code>utm_campaign</code>.</p> <p>In the Google Analytics report, the campaign data can be found under <strong>Acquisition &gt; Campaigns &gt; All Campaigns</strong>.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/UzXLUXmjGg-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/UzXLUXmjGg-780.webp 780w"><img alt="Campaigns Google Analytics Report" loading="lazy" decoding="async" src="https://bitsofco.de/img/UzXLUXmjGg-780.png" width="780" height="499"></picture></p> <p>You can view the full code for the buttons on <a href="https://github.com/ireade/custom-social-buttons">my github</a>.</p> Working with Colour in CSS 2015-08-18T00:00:00Z https://bitsofco.de/working-with-colour-in-css/ <p>Similarly to how we have <a href="https://bitsofco.de/css-font-sizing">different units for font sizes</a>, there are a number of different ways we can define a colour in css.</p> <p>There are currently four css properties that accept a colour as a value - <code>color</code>, <code>background-color</code>, <code>border-color</code>, and <code>outline-color</code>. For each of these properties, the colour value can be any of the following -</p> <ul> <li>A named keyword, e.g. <code>black</code></li> <li>An rgb or rgba unit, e.g. <code>rgb(0, 0, 0)</code></li> <li>An hsl or hsla unit, eg. <code>hsl(0, 0%, 0%)</code></li> <li><code>transparent</code></li> <li><code>currentColor</code></li> <li><code>inherit</code></li> </ul> <h2 id="named-keywords" tabindex="-1">Named keywords <a class="header-anchor" href="https://bitsofco.de/working-with-colour-in-css/">#</a></h2> <p>Named keywords are plain English names for certain colours which have been predefined within css. Modern browsers understand these keywords in the same way that they understand the other numeric inputs.</p> <p>There are currently <a href="https://www.w3.org/TR/css3-color/#svg-color">145 of these keywords</a>, each with a corresponding numeric RGB value.</p> <p>Using a named keyword is as simple as writing -</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">color</span><span class="token punctuation">:</span> plum<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="rgb-and-rgba" tabindex="-1">rgb and rgba <a class="header-anchor" href="https://bitsofco.de/working-with-colour-in-css/">#</a></h2> <h3 id="rgb" tabindex="-1">rgb <a class="header-anchor" href="https://bitsofco.de/working-with-colour-in-css/">#</a></h3> <p>The RGB format is the most popular way of defining colour in CSS. The RGB colour model works by declaring specific levels of red, green and blue.</p> <p>There are four formats in which RGB can be written in for css -</p> <table> <thead> <tr> <th>Notation</th> <th>Description</th> <th>Example - Black</th> </tr> </thead> <tbody> <tr> <td>Decimal</td> <td>Value between 0 and 255 for each primary colour</td> <td><code>rgb(0, 0, 0)</code></td> </tr> <tr> <td>Percentage</td> <td>A percentage value (0-100%) for each primary colour</td> <td><code>rgb(0%, 0%, 0%)</code></td> </tr> <tr> <td>Hexadecimal(6-digit)</td> <td>A 6-digit format forming a unique code for each colour combination</td> <td><code>#000000</code></td> </tr> <tr> <td>Hexadecimal(3-digit)</td> <td>Shorthand for 6-digit hexadecimal notation</td> <td><code>#000</code></td> </tr> </tbody> </table> <p>The 3-digit hexadecimal notation is a shorthand for certain 6-digit values which have repeating elements. The 3-digit version is converted into the full 6-digit version by duplicating each of the three digits. For example, <code>#fb0</code> becomes <code>#ffbb00</code>.</p> <h3 id="rgba" tabindex="-1">rgba <a class="header-anchor" href="https://bitsofco.de/working-with-colour-in-css/">#</a></h3> <p>RGBA is an extension of RGB to allow the specification of transparency within the same function. Only the decimal and percentage notations can be used with RGBA.</p> <table> <thead> <tr> <th>Notation</th> <th>Example - Black, 0.5 Opacity</th> </tr> </thead> <tbody> <tr> <td>Decimal</td> <td><code>rgba(0, 0, 0, 0.5)</code></td> </tr> <tr> <td>Percentage</td> <td><code>rgba(0%, 0%, 0%, 0.5)</code></td> </tr> </tbody> </table> <p>The RGBA unit is treated as a completely separate unit to RGB. This means that, for browsers that do not support RGBA (notably IE 8) the entire function will be disregarded. It is not the case that the opacity section at the end will just be ignored.</p> <p>If you need to support these browsers, you will need to provide a solid colour fallback. For example -</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">color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* fallback for IE8 */</span><br> <span class="token property">color</span><span class="token punctuation">:</span> <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.5<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="hsl-and-hsla" tabindex="-1">hsl and hsla <a class="header-anchor" href="https://bitsofco.de/working-with-colour-in-css/">#</a></h2> <h3 id="hsl" tabindex="-1">hsl <a class="header-anchor" href="https://bitsofco.de/working-with-colour-in-css/">#</a></h3> <p>The HSL model is an alternative to RGB. It works by specifying three dimensions - hue, saturation and lightness.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/zjNQcnbfms-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/zjNQcnbfms-780.webp 780w"><img alt="HSL Colour Wheel" loading="lazy" decoding="async" src="https://bitsofco.de/img/zjNQcnbfms-780.png" width="780" height="554"></picture></p> <table> <thead> <tr> <th>Dimension</th> <th>Range</th> <th>Key Values</th> </tr> </thead> <tbody> <tr> <td>Hue</td> <td>0 - 360</td> <td>0/360 = red120 = green240 = blue</td> </tr> <tr> <td>Saturation</td> <td>0 - 100%</td> <td>0% = grey100% = full colour</td> </tr> <tr> <td>Lightness</td> <td>0 - 100%</td> <td>0% = black50% = normal100% = white</td> </tr> </tbody> </table> <p>The <strong>hue</strong> value is represented by the angle on the colour wheel. From this value, we can determine what colour - red, green, blue, or a mix between them - we are working with.</p> <p>The <strong>saturation</strong> value is represented by the distance from the axis. It detemins the strength of the colour, with the lowest saturation being closer to the centre of the circle.</p> <p>The <strong>lightness</strong> value is represented by the z-axis, the depth of the cylinder. This percentage value determines the shade of the colour, with 0% being completely black, 100%, being completely white, and 50%, being normal.</p> <p>HSL is seen as a more intuitive format for colour because we are able to reason what a colour might be just from looking at the numeric value. For example, a pastel blue could be -</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-color</span><span class="token punctuation">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span>200<span class="token punctuation">,</span> 90%<span class="token punctuation">,</span> 70%<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Pastel Blue</p> <h3 id="hsla" tabindex="-1">hsla <a class="header-anchor" href="https://bitsofco.de/working-with-colour-in-css/">#</a></h3> <p>Like RGBA, the HSLA unit is an extension of HSL to allow the specification of transparency within the same function. The opacity unit is added as a fourth argument to the function.</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">color</span><span class="token punctuation">:</span> <span class="token function">hsla</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 100%<span class="token punctuation">,</span> 50%<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h2 id="transparent" tabindex="-1">transparent <a class="header-anchor" href="https://bitsofco.de/working-with-colour-in-css/">#</a></h2> <p>The <code>transparent</code> value for a colour-related css property can be used to specify a completely opaque colour.</p> <p>It is treated in a similar way to the named keywords, in that it is essentially a shorthand for writing an RGBA value. What is actually computed when we use this value is <code>rgba(0, 0, 0, 0)</code>.</p> <h2 id="currentcolor" tabindex="-1">currentColor <a class="header-anchor" href="https://bitsofco.de/working-with-colour-in-css/">#</a></h2> <p>The <code>currentColor</code> value is a keyword defined in CSS3. It was added to finally formalise a behaviour in CSS in which the <code>border-color</code> of an element was, by default, the same colour as the <code>color</code> property.</p> <p>Now that is has been added as a value itself, it can be used on any of the colour related properties. Depending on which property it is set to, it has different meanings -</p> <table> <thead> <tr> <th>Property</th> <th>Computed value</th> </tr> </thead> <tbody> <tr> <td><code>background-color``border-color``outline-color</code></td> <td>Same value as the <code>color</code> property on the same element</td> </tr> <tr> <td><code>color</code></td> <td><code>inherit</code></td> </tr> </tbody> </table> <p>For example -</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">color</span><span class="token punctuation">:</span> #000<span class="token punctuation">;</span><br> <span class="token property">background-color</span><span class="token punctuation">:</span> currentColor<span class="token punctuation">;</span> <span class="token comment">/* computed value of #000 */</span><br><span class="token punctuation">}</span></code></pre> <h2 id="inherit" tabindex="-1">inherit <a class="header-anchor" href="https://bitsofco.de/working-with-colour-in-css/">#</a></h2> <p>This value specifies that the element should take the same colour as it's parent.</p> How Floating Works 2015-08-11T00:00:00Z https://bitsofco.de/how-floating-works/ <p>Although I very rarely use floats nowadays, there are certain specific circumstances in which they are the only viable solution. In these rare cases, I find myself getting frustrated because they don't work in the way I think they should. So I decided to look into the rules that govern the behaviour of floats, and how to properly use them.</p> <h2 id="the-rules-of-floats" tabindex="-1">The Rules of Floats <a class="header-anchor" href="https://bitsofco.de/how-floating-works/">#</a></h2> <p>There are four values the float property can be set to -</p> <pre><code>.foo { float: left | right | inherit | none } </code></pre> <p>What each of these values mean for how the floated element will be positioned within its parent and the document in general is determined by the following rules -</p> <h3 id="1-a-floated-element-is-invisible-to-its-parent" tabindex="-1">1 - A floated element is &quot;invisible&quot; to its parent <a class="header-anchor" href="https://bitsofco.de/how-floating-works/">#</a></h3> <p>For all intents and purposes, <strong>a floated element does not exist to its parent element</strong>. If a parent element has only floated children within it, it will collapse as though it is empty. This is similar to how a parent element behaves when it has an element absolutely positioned within it.</p> <pre><code>.parent { position: relative; padding: 10px; } .child { float: left } </code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/CwUWb6CL17-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/CwUWb6CL17-780.webp 780w"><img alt="Parent element collapses with only floated children element" loading="lazy" decoding="async" src="https://bitsofco.de/img/CwUWb6CL17-780.png" width="780" height="166"></picture></p> <h3 id="2-a-left-right-floated-element-will-try-to-be-as-close-to-the-top-and-left-right-of-its-parent-element-as-possible" tabindex="-1">2 - A left/right floated element will try to be as close to the top and left/right of its parent element as possible <a class="header-anchor" href="https://bitsofco.de/how-floating-works/">#</a></h3> <p>This is the &quot;optimal&quot; position that a left/right floated element tries to achieve.</p> <h3 id="3-previously-defined-elements-will-push-a-floated-element-down" tabindex="-1">3 - Previously defined elements will push a floated element down <a class="header-anchor" href="https://bitsofco.de/how-floating-works/">#</a></h3> <p>Although a floated element will try to be as close to the top of the parent element as possible, <strong>any sibling elements previously defined in the document will push the floated element down</strong>. It doesn't matter if the previously defined element is a block element or inline (if it takes up the full width of the parent).</p> <p>This means that if we have a paragraph defined before or after the floating element, we will get different results.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/LSsP-gEpC5-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/LSsP-gEpC5-780.webp 780w"><img alt="Floated element defined before the paragraph" loading="lazy" decoding="async" src="https://bitsofco.de/img/LSsP-gEpC5-780.png" width="780" height="176"></picture> Floated Element Defined <strong>Before</strong> Paragraph</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/thx5v4uh0O-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/thx5v4uh0O-780.webp 780w"><img alt="Floated element defined before the paragraph" loading="lazy" decoding="async" src="https://bitsofco.de/img/thx5v4uh0O-780.png" width="780" height="209"></picture> Floated Element Defined After Paragraph</p> <h3 id="4-a-previously-declared-floated-element-will-be-given-a-more-preferable-position" tabindex="-1">4 - A previously declared floated element will be given a more preferable position <a class="header-anchor" href="https://bitsofco.de/how-floating-works/">#</a></h3> <p>The most preferable position explained in Rule 2 will be given to the floated element first defined in the markup. With right floating elements, for example, the first element defined in the HTML will be the furthest right because that is the most optimal position.</p> <pre><code>&lt;div class=&quot;container&quot;&gt; &lt;div class=&quot;right&quot;&gt;1&lt;/div&gt; &lt;div class=&quot;right&quot;&gt;2&lt;/div&gt; &lt;div class=&quot;right&quot;&gt;3&lt;/div&gt; &lt;p&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit...&lt;/p&gt; &lt;/div&gt; </code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/erCNoRJ9db-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/erCNoRJ9db-780.webp 780w"><img alt="Multiple elements floating right" loading="lazy" decoding="async" src="https://bitsofco.de/img/erCNoRJ9db-780.png" width="780" height="198"></picture></p> <h3 id="5-a-position-closer-to-the-top-of-the-parent-element-is-preferred-to-a-position-closer-to-the-left-right-of-the-parent-element" tabindex="-1">5 - A position closer to the top of the parent element is preferred to a position closer to the left/right of the parent element <a class="header-anchor" href="https://bitsofco.de/how-floating-works/">#</a></h3> <p>When there are multiple elements floating in the same direction, the <strong>subsequent elements will choose a position further away from the left/right of the parent element in order to be closer to the top of the parent element</strong>.</p> <p>This means that multiple floating elements will line up side by side where possible, and will only move down below each other if the parent element's width cannot contain them.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/mjvBNjZeqY-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/mjvBNjZeqY-780.webp 780w"><img alt="Multiple floated elements move closer to the top" loading="lazy" decoding="async" src="https://bitsofco.de/img/mjvBNjZeqY-780.png" width="780" height="339"></picture></p> <p>In this example, element 2 is in a more preferrable position than element 3.</p> <h3 id="6-a-floated-element-should-not-extend-outside-its-parent-element" tabindex="-1">6 - A floated element should not extend outside its parent element <a class="header-anchor" href="https://bitsofco.de/how-floating-works/">#</a></h3> <p>An element floated left <strong>cannot</strong> extend outside the left outer edge of its parent element.</p> <p>An element floated left <strong>should not</strong> extend outside the right outer edge of its parent element unless there is no room left.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/YM83nNLnok-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/YM83nNLnok-780.webp 780w"><img alt="Floating element shouldn't exted outside parent" loading="lazy" decoding="async" src="https://bitsofco.de/img/YM83nNLnok-780.png" width="780" height="262"></picture></p> <h2 id="clearing" tabindex="-1">Clearing <a class="header-anchor" href="https://bitsofco.de/how-floating-works/">#</a></h2> <p>The <code>clear</code> property goes hand in hand with <code>float</code>. It allows us to break the change in document flow that a floating element causes. There are three values to the clear property -</p> <pre><code>.foo { clear: left | right | both } </code></pre> <p>When an element is set to <code>clear: left</code>, this means that <strong>the clearing element's top edge must be below any left floated elements's bottom edge</strong>. If the element is clearing both, then it's top edge must be below any floated elements.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/RNvks5TMn8-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/RNvks5TMn8-780.webp 780w"><img alt="Paragraph clearing left" loading="lazy" decoding="async" src="https://bitsofco.de/img/RNvks5TMn8-780.png" width="780" height="223"></picture> Paragraph Clearing Left</p> <p>If an element is only clearing left or right, <strong>elements floating in the other direction are not affected</strong> by this.</p> <pre><code>&lt;div class=&quot;container&quot;&gt; &lt;div class=&quot;left&quot;&gt;1&lt;/div&gt; &lt;div class=&quot;left&quot;&gt;2&lt;/div&gt; &lt;div class=&quot;left&quot;&gt;3&lt;/div&gt; &lt;div class=&quot;right&quot;&gt;1&lt;/div&gt; &lt;div class=&quot;right&quot;&gt;2&lt;/div&gt; &lt;div class=&quot;right&quot;&gt;3&lt;/div&gt; &lt;p class=&quot;clear-left&quot;&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit...&lt;/p&gt; &lt;/div&gt; </code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/xkHPbFuvlR-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/xkHPbFuvlR-780.webp 780w"><img alt="Paragraph only clearing left" loading="lazy" decoding="async" src="https://bitsofco.de/img/xkHPbFuvlR-780.png" width="780" height="306"></picture></p> <h3 id="clearfix" tabindex="-1">Clearfix <a class="header-anchor" href="https://bitsofco.de/how-floating-works/">#</a></h3> <p>There has previously been a lot of confusion over the proper way to clear. Especially due to issues with legacy browsers. A popular clearfix solution that I use was coined by <a href="https://cssmojo.com/latest_new_clearfix_so_far/">CSS Mojo</a>. The following styles are applied to the parent element of the floated properties or any subsequently defined elements -</p> <pre><code>.cf::after { content:&quot;&quot;; display:table; clear:both; } </code></pre> <p>You can read their <a href="https://cssmojo.com/latest_new_clearfix_so_far/">blog post</a> on why they use these properties in this way, but this solution essentially creates an invisible <a href="https://bitsofco.de/pseudo-classes-pseudo-elements-and-colon-notation">pseudo-element</a> for the purpose of clearing.</p> Building Designer News for Pebble 2015-08-04T00:00:00Z https://bitsofco.de/building-designer-news-for-pebble/ <p>A few weeks ago, I got a Pebble Time smartwatch! I was looking into the developer documentation, and it turns out they have a Pebble.js library (although still in beta), which allows you to write apps for Pebble entirely in Javascript. So, I decided to try making a simple app for Pebble that pulls the top 10 stories from Designer News.</p> <p>Here is the final product:</p> <p><img src="https://www.filepicker.io/api/file/wN8G53bR4uSSzrLGN8cw/convert?h=168&amp;w=144" alt="Pebble for DN App Screenshot"> <img src="https://www.filepicker.io/api/file/fNyxpqUgQ6aKury2lfcF/convert?h=168&amp;w=144" alt="Pebble for DN App Screenshot"> <img src="https://www.filepicker.io/api/file/yQ1CT6uXT2Wvr3kgAXoA/convert?h=168&amp;w=144" alt="Pebble for DN App Screenshot"></p> <pre><code>&lt;br&gt; &lt;a href=&quot;https://apps.getpebble.com/applications/55b75efd3d39bb179a00003d&quot;&gt;Download from the Pebble App Store&lt;/a&gt; / &lt;a href=&quot;https://github.com/ireade/dn-for-pebble&quot;&gt;View on Github&lt;/a&gt; </code></pre> <h2 id="intro-to-pebble-js" tabindex="-1">Intro to Pebble.js <a class="header-anchor" href="https://bitsofco.de/building-designer-news-for-pebble/">#</a></h2> <p>Getting started developing apps and watchfaces for the Pebble is really simple. If you do not want to deal with the SDK, you can sign up for <a href="https://cloudpebble.net">CloudPebble</a>, their online IDE. This allows you to get right into the code without worrying about any setup.</p> <p><a href="https://developer.getpebble.com/docs/pebblejs">Pebble.js</a>, as you would imagine, is very similar to writing regular javascript, but with some additional features. There were three in-built Pebble.js libraries I used in making this application - UI, Vector2, and ajax.</p> <h3 id="ui-library" tabindex="-1">UI Library <a class="header-anchor" href="https://bitsofco.de/building-designer-news-for-pebble/">#</a></h3> <p>This library allows us to create Pebble-specific UI elements. In this app, I used the following elements -</p> <ul> <li><code>Window</code> - creates a new screen</li> <li><code>Menu</code> - creates a menu-format screen with a list of actionable items</li> <li><code>Rect</code> - creates a rectangle object</li> <li><code>Text</code> - creates a text element</li> </ul> <p>To create a new window, for example, all we need to do is write -</p> <pre><code>var windowName = new UI.Window({ // options }); </code></pre> <h3 id="vector2-library" tabindex="-1">Vector2 Library <a class="header-anchor" href="https://bitsofco.de/building-designer-news-for-pebble/">#</a></h3> <p>This library allows us to construct a 2 dimensional vector. So, to create a rectangle, we write -</p> <pre><code>var rectangle = new UI.Rect({ size: new Vector2(100, 50) // (x, y) // more options }) </code></pre> <h3 id="ajax-library" tabindex="-1">Ajax Library <a class="header-anchor" href="https://bitsofco.de/building-designer-news-for-pebble/">#</a></h3> <p>This library provides a simple way to make ajax requests. For example -</p> <pre><code>ajax({ url: requestUrl, type: 'json' }, function(data, status, request) { console.log(data); }, function(error, status, request) { console.log('The ajax request failed: ' + error); }); </code></pre> <h2 id="designer-news-for-pebble" tabindex="-1">Designer News for Pebble <a class="header-anchor" href="https://bitsofco.de/building-designer-news-for-pebble/">#</a></h2> <p>Using these libraries, I was able to create this very simple application. Although I have made some improvements to the app recently, I thought it would be more useful to show the version 1.0, which gives a more basic and clear idea of how everything works.</p> <p>Here is my very short and simple app file for version 1.0 -</p> <pre><code>/* * DN for Pebble * Ire Aderinokun */ // Require dependencies var UI = require('ui'); var ajax = require('ajax'); var Vector2 = require('vector2'); // Loading Window var loadingWindow = new UI.Window({ fullscreen: true }); var loadingBg = new UI.Rect({ position: new Vector2(0, 0), size: new Vector2(144, 168), // full width and height of pebble window backgroundColor: 'blue', // in-built colours }); var loadingText = new UI.Text({ position: new Vector2(0, 50), size: new Vector2(144, 50), text:'DN Loading', font:'GOTHIC_24', // in-built font color:'white', textOverflow:'wrap', textAlign:'center', backgroundColor:'clear' }); // Add background and text elements to the window loadingWindow.add(loadingBg); loadingWindow.add(loadingText); // Show the window loadingWindow.show(); // DN Request URL var dnRequestURL = 'https://api.import.io/store/connector/_magic?url=https%3A%2F%2Fwww. designernews.co%2F%3Fformat%3Drss&amp;js=false&amp;_user=59cf7cce-4fdd-4a7f-be6f-630574c6d814 &amp;_apikey='+apiKey; // Function to parse the JSON object returned var parseFeed = function(data, quantity) { var items = []; for(var i = 0; i &lt; quantity; i++) { var title = data.tables[0].results[i].value_1; items.push({ title: title }); } return items; }; // AJAX request ajax( { url: dnRequestURL, type:'json' }, // On Success function(data) { // Create Menu of 10 Top Stories var menuItems = parseFeed(data, 10); var resultsMenu = new UI.Menu({ backgroundColor: 'white', textColor: 'black', highlightBackgroundColor: 'blue', highlightTextColor: 'white', sections: [{ title: 'Top Stories', items: menuItems }] }); // When loaded, show resultsMenu and hide loading window resultsMenu.show(); loadingWindow.hide(); // On Select of a particular story resultsMenu.on('select', function(e) { var story = data.tables[0].results[e.itemIndex]; var storyTitle = story.value_1; var storyDescription = story.description; // Create window for the detail view of story var detailView = new UI.Window({ scrollable: true, fullscreen: true }); var headerBg = new UI.Rect({ position: new Vector2(0, 0), size: new Vector2(144, 105), backgroundColor: 'blue' }); var bodyBg = new UI.Rect({ position: new Vector2(0, 105), size: new Vector2(144, 320), backgroundColor: 'white' }); var Title = new UI.Text({ position: new Vector2(8, 0), size: new Vector2(131, 80), text: storyTitle, font:'GOTHIC_24_BOLD', color:'white', textOverflow:'wrap', textAlign:'left', backgroundColor:'clear' }); var Description = new UI.Text({ position: new Vector2(8, 110), size: new Vector2(131, 300), text: storyDescription, font:'GOTHIC_18', color:'black', textOverflow:'wrap', textAlign:'left', backgroundColor:'clear' }); detailView.add(headerBg); detailView.add(bodyBg); detailView.add(Title); detailView.add(Description); detailView.show(); }); }, // On error function(error) { var errorText = new UI.Text({ position: new Vector2(0, 50), size: new Vector2(144, 100), text:'Download failed: ' + error, font:'GOTHIC_24', color:'white', textOverflow:'wrap', textAlign:'center', backgroundColor:'clear' }); // remove loading text and add error text loadingWindow.remove(loadingText); loadingWindow.add(errorText); } ); </code></pre> <p>To get a JSON version of the website feed, I used <a href="https://import.io">import.io</a>.</p> <p>That's it! I have since made some design improvements to the app, mainly adding a variable height for the header section depending on the length of the story title. I'm also working on adding support for recent stories and discussions.</p> <p>The only downside to Pebble.js is that it actually runs on the phone and so cannot be used when the phone is out of range. Although for an app like this, that isn't a problem, it is something worth noting.</p> A Simple Gulp Workflow 2015-07-28T00:00:00Z https://bitsofco.de/a-simple-gulp-workflow/ <p><a href="https://gulpjs.com">Gulp</a>, as you may have heard, is a javascript &quot;task runner&quot; that allows us to automate certain tasks in our development workflow. We use it by defining what tasks we want to automate in a <code>gulpfile.js</code>, and running that through the command line.</p> <p>Although there are several other similar task runners, I chose to use Gulp because it is javascript-based and, because of its extensive <a href="https://gulpjs.com/plugins/">library of plugins</a> and incredibly <a href="https://github.com/gulpjs/gulp/blob/master/docs/API.md">simple API</a>, it can be easily extended to do pretty much anything you can think of to automate.</p> <p>If you are just getting started, it can be as simple as you need it to be. When I first started using Gulp, all I wanted and needed it for was -</p> <ul> <li>To compile and minify my SASS files, and</li> <li>To concatenate and minify my various javascript files.</li> </ul> <p>Here is the simple gulp workflow I used to accomplish this.</p> <h2 id="installing-gulp-and-plugins" tabindex="-1">Installing Gulp and Plugins <a class="header-anchor" href="https://bitsofco.de/a-simple-gulp-workflow/">#</a></h2> <p>To get started, <strong>install gulp globally</strong> by typing the following command into your terminal -</p> <pre><code>$ npm install --global gulp </code></pre> <p>If you are using a Mac, you may need to prepend this statement with a <code>sudo</code> command. Next, while in your project directory, <strong>install gulp locally</strong> as a developer dependency -</p> <pre><code>$ npm install --save-dev gulp </code></pre> <p>Now, we can <strong>install any plugins</strong> needed. To install a plugin, we type -</p> <pre><code>$ npm install --save-dev [plugin-name] </code></pre> <p>These are the plugins I used for this simple workflow -</p> <table> <thead> <tr> <th>Plugin</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><a href="https://www.npmjs.com/package/gulp-sass/"><code>gulp-sass</code></a></td> <td>Compiles SASS to CSS</td> </tr> <tr> <td><a href="https://www.npmjs.com/package/gulp-concat/"><code>gulp-concat</code></a></td> <td>Concatenates files</td> </tr> <tr> <td><a href="https://www.npmjs.com/package/gulp-uglify/"><code>gulp-uglify</code></a></td> <td>Minifies js files</td> </tr> <tr> <td><a href="https://www.npmjs.com/package/gulp-util/"><code>gulp-util</code></a></td> <td>Utility functions. For example, outputs descriptive error messages in the terminal</td> </tr> </tbody> </table> <h2 id="the-gulpfile" tabindex="-1">The Gulpfile <a class="header-anchor" href="https://bitsofco.de/a-simple-gulp-workflow/">#</a></h2> <p>Once we have installed gulp and any plugins, we need to create a <code>gulpfile.js</code> in the root of the project directory.</p> <p>A very basic gulpfile could look like this -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// First require gulp</span><br><span class="token keyword">var</span> gulp <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Define default task</span><br>gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'default'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</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">"Hello, world!"</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>For this simple workflow, this is my gulpfile -</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token comment">// Require gulp and plugins</span><br><span class="token keyword">var</span> gulp <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp'</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br> sass <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp-sass'</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br> concat <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp-concat'</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br> uglify <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp-uglify'</span><span class="token punctuation">)</span><span class="token punctuation">,</span><br> gutil <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp-util'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><br><span class="token comment">// Define file sources</span><br><span class="token keyword">var</span> sassMain <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'development/sass/main.scss'</span><span class="token punctuation">]</span><span class="token punctuation">;</span><br><span class="token keyword">var</span> sassSources <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'development/sass/**/*.scss'</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Any .scss file in any sub-directory</span><br><span class="token keyword">var</span> jsSources <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'development/scripts/*.js'</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token comment">// Any .js file in scripts directory</span><br><br><br><span class="token comment">// Task to compile SASS files</span><br>gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'sass'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> gulp<span class="token punctuation">.</span><span class="token function">src</span><span class="token punctuation">(</span>sassMain<span class="token punctuation">)</span> <span class="token comment">// use sassMain file source</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span><span class="token function">sass</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">outputStyle</span><span class="token operator">:</span> <span class="token string">'compressed'</span> <span class="token comment">// Style of compiled CSS</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br> <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> gutil<span class="token punctuation">.</span>log<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// Log descriptive errors to the terminal</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span>gulp<span class="token punctuation">.</span><span class="token function">dest</span><span class="token punctuation">(</span><span class="token string">'assets/css'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// The destination for the compiled file</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><br><span class="token comment">// Task to concatenate and uglify js files</span><br>gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'concat'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> gulp<span class="token punctuation">.</span><span class="token function">src</span><span class="token punctuation">(</span>jsSources<span class="token punctuation">)</span> <span class="token comment">// use jsSources</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span><span class="token function">concat</span><span class="token punctuation">(</span><span class="token string">'script.js'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// Concat to a file named 'script.js'</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span><span class="token function">uglify</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// Uglify concatenated file</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span>gulp<span class="token punctuation">.</span><span class="token function">dest</span><span class="token punctuation">(</span><span class="token string">'assets/js'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// The destination for the concatenated and uglified file</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><br><span class="token comment">// Task to watch for changes in our file sources</span><br>gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'watch'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> gulp<span class="token punctuation">.</span><span class="token function">watch</span><span class="token punctuation">(</span>sassMain<span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token string">'sass'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// If any changes in 'sassMain', perform 'sass' task</span><br> gulp<span class="token punctuation">.</span><span class="token function">watch</span><span class="token punctuation">(</span>sassSources<span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token string">'sass'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> gulp<span class="token punctuation">.</span><span class="token function">watch</span><span class="token punctuation">(</span>jsSources<span class="token punctuation">,</span><span class="token punctuation">[</span><span class="token string">'concat'</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><br><span class="token comment">// Default gulp task</span><br>gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'default'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">'sass'</span><span class="token punctuation">,</span> <span class="token string">'concat'</span><span class="token punctuation">,</span> <span class="token string">'watch'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>With this workflow, my folder structure will be like this -</p> <pre><code>project/ - gulpfile.js - index.html - development/ - sass/ - main.scss - _partial.scss - folder/ - // more partials.scss - scripts/ - // various files.js - assets/ - css/ - main.css - js/ - script.js </code></pre> <p>To start the gulp default task, all we have to do is run the following command while in the root of the project directory -</p> <pre><code>$ gulp </code></pre> <p>We can also run specific gulp tasks by writing -</p> <pre><code>$ gulp [task-name] </code></pre> <h2 id="some-useful-plugin-features" tabindex="-1">Some Useful Plugin Features <a class="header-anchor" href="https://bitsofco.de/a-simple-gulp-workflow/">#</a></h2> <p>As I mentioned, gulp can be used for much more than just compilation and concatenation. Here are a some useful features and the plugins you can use to acheive them.</p> <h3 id="run-a-local-server-with-live-reload" tabindex="-1">Run A Local Server With Live Reload <a class="header-anchor" href="https://bitsofco.de/a-simple-gulp-workflow/">#</a></h3> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> connect <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp-connect'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'serve'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> connect<span class="token punctuation">.</span><span class="token function">server</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">port</span><span class="token operator">:</span> <span class="token number">8888</span><span class="token punctuation">,</span><br> <span class="token literal-property property">livereload</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><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>With this plugin, we can also specify that the server reload when another task is finished running. For example -</p> <pre class="language-js" tabindex="0"><code class="language-js">gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'sass'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> gulp<span class="token punctuation">.</span><span class="token function">src</span><span class="token punctuation">(</span>sassMain<span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span><span class="token function">sass</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">outputStyle</span><span class="token operator">:</span> <span class="token string">'compressed'</span><br> <span class="token punctuation">}</span><span class="token punctuation">)</span><br> <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> gutil<span class="token punctuation">.</span>log<span class="token punctuation">)</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span>gulp<span class="token punctuation">.</span><span class="token function">dest</span><span class="token punctuation">(</span><span class="token string">'assets/css'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span>connect<span class="token punctuation">.</span><span class="token function">reload</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Reload</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <h3 id="minify-html" tabindex="-1">Minify HTML <a class="header-anchor" href="https://bitsofco.de/a-simple-gulp-workflow/">#</a></h3> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> minifyHTML <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp-minify-html'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'html'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> gulp<span class="token punctuation">.</span><span class="token function">src</span><span class="token punctuation">(</span>htmlSources<span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span><span class="token function">minifyHTML</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 function">pipe</span><span class="token punctuation">(</span>gulp<span class="token punctuation">.</span><span class="token function">dest</span><span class="token punctuation">(</span><span class="token string">''</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> <h3 id="publish-repo-to-github-pages" tabindex="-1">Publish Repo To Github Pages <a class="header-anchor" href="https://bitsofco.de/a-simple-gulp-workflow/">#</a></h3> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> ghPages <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp-gh-pages'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'deploy'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> gulp<span class="token punctuation">.</span><span class="token function">src</span><span class="token punctuation">(</span><span class="token string">'./dist/**/*'</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span><span class="token function">ghPages</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></code></pre> <h3 id="convert-markdown-to-html" tabindex="-1">Convert Markdown to HTML <a class="header-anchor" href="https://bitsofco.de/a-simple-gulp-workflow/">#</a></h3> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> markdown <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp-markdown'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'md'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">return</span> gulp<span class="token punctuation">.</span><span class="token function">src</span><span class="token punctuation">(</span><span class="token string">'*.md'</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span><span class="token function">markdown</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 function">pipe</span><span class="token punctuation">(</span>gulp<span class="token punctuation">.</span><span class="token function">dest</span><span class="token punctuation">(</span><span class="token string">'dist'</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> <h3 id="lint-your-css-files" tabindex="-1">Lint Your CSS Files <a class="header-anchor" href="https://bitsofco.de/a-simple-gulp-workflow/">#</a></h3> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token keyword">var</span> csslint <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'gulp-csslint'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br>gulp<span class="token punctuation">.</span><span class="token function">task</span><span class="token punctuation">(</span><span class="token string">'css-lint'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br> gulp<span class="token punctuation">.</span><span class="token function">src</span><span class="token punctuation">(</span><span class="token string">'assets/css/*.css'</span><span class="token punctuation">)</span><br> <span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span><span class="token function">csslint</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 function">pipe</span><span class="token punctuation">(</span>csslint<span class="token punctuation">.</span><span class="token function">reporter</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></code></pre> <p>You can check out all the gulp plugins in their <a href="https://gulpjs.com/plugins/">plugin directory</a>.</p> Hiding Elements With CSS 2015-07-21T00:00:00Z https://bitsofco.de/hiding-elements-with-css/ <p>Being able to hide and show elements on a page is an integral part of responsive design. Depending on what you want to achieve, there are many different ways to hide an element using CSS. In this article, I explore the properties we can use to do this, and the pros and cons of each.</p> <h2 id="the-properties" tabindex="-1">The Properties <a class="header-anchor" href="https://bitsofco.de/hiding-elements-with-css/">#</a></h2> <h3 id="display" tabindex="-1">Display <a class="header-anchor" href="https://bitsofco.de/hiding-elements-with-css/">#</a></h3> <p>As <a href="https://bitsofco.de/controlling-the-box-model">I have mentioned before</a>, every element in an HTML document is a rectangular box. Hiding an element with the display property means that this rectangular box is not generated at all.</p> <p>Although the element is still within the markup, (which you can see if you use inspect element), for all intents and purposes, it doesn't exist on the page. No part of the box model - the content area, padding area, border area or margin area - is generated or appears on the page.</p> <p>Any content within the element, or its children, is functionally non-existent. If it is an actionable element, for example a <code>button</code> or <code>a</code>, it cannot be acted upon. The element and any content within it is also ignored by screen readers.</p> <pre><code>.foo { display: none; } </code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/HRnMKuP0fZ-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/HRnMKuP0fZ-780.webp 780w"><img alt="Hiding Elements with Display CSS Property" loading="lazy" decoding="async" src="https://bitsofco.de/img/HRnMKuP0fZ-780.png" width="780" height="354"></picture></p> <h3 id="visibility" tabindex="-1">Visibility <a class="header-anchor" href="https://bitsofco.de/hiding-elements-with-css/">#</a></h3> <p>With the visibility property, the &quot;rectangular box&quot; is still generated, but not rendered on the screen. Because the box is generated, the four areas that make up the box model still affect the layout of the rest of the page. However, the four areas are not visible on the screen.</p> <p>Apart from the fact that the box model is generated, using the visibility property to hide an element is similar to the display property. The element cannot be acted upon and is not read by screen readers.</p> <pre><code>.foo { visibility: hidden; } </code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/FFi5EWeCkp-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/FFi5EWeCkp-780.webp 780w"><img alt="Hiding Elements with Visibility CSS Property" loading="lazy" decoding="async" src="https://bitsofco.de/img/FFi5EWeCkp-780.png" width="780" height="350"></picture></p> <h3 id="opacity" tabindex="-1">Opacity <a class="header-anchor" href="https://bitsofco.de/hiding-elements-with-css/">#</a></h3> <p>The opacity property only deals with how an element appears visually; more specifically, its transparency.</p> <p>When an element's opacity is set to the absolute lowest, although it becomes hidden visually on the screen, it is functionally no different to if the element were set to any solid colour. The element still takes up as much space as it usually would, is still read by screen readers, and can still be interacted with.</p> <pre><code>.foo { opacity: 0; } </code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/FFi5EWeCkp-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/FFi5EWeCkp-780.webp 780w"><img alt="Hiding Elements with Opacity CSS Property" loading="lazy" decoding="async" src="https://bitsofco.de/img/FFi5EWeCkp-780.png" width="780" height="350"></picture></p> <h3 id="position" tabindex="-1">Position <a class="header-anchor" href="https://bitsofco.de/hiding-elements-with-css/">#</a></h3> <p>The position property isn't typically meant for hiding and showing elements on the page. The use of this property for this purpose came out of a need to be able to have an element be visually hidden on a page and not affect the layout, but still be readable by screen readers and actionable if needed.</p> <p>There are two ways in which the position property cn be combined with other properites to achieve this.</p> <p>Combined with the <code>top</code> and <code>left</code> properties, the element is moved out of the viewport and therefore out of eyesight.</p> <pre><code>.foo { position: absolute; top: -9999px; left: -9999px; } </code></pre> <p>Alternatively, <a href="https://adaptivethemes.com/using-css-clip-as-an-accessible-method-of-hiding-content">Jeff Burnz</a> pioneered the idea of using the <code>clip</code> property to resize the rectangular box to an insignificant size, making it functionally hidden.</p> <pre><code>.foo { position: absolute; clip: rect(1px 1px 1px 1px); /* syntax for IE6 &amp; IE7 */ clip: rect(1px, 1px, 1px, 1px); } </code></pre> <p>With both methods, the result is the same -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/HRnMKuP0fZ-780.avif 780w"><source type="image/webp" srcset="https://bitsofco.de/img/HRnMKuP0fZ-780.webp 780w"><img alt="Hiding Elements with Position CSS Property" loading="lazy" decoding="async" src="https://bitsofco.de/img/HRnMKuP0fZ-780.png" width="780" height="354"></picture></p> <p>Although these solutions are a bit of a hacky way to deal with the problem, they are currently the best way to remove an element visually from the screen, while maintaining its function.</p> <h2 id="summary" tabindex="-1">Summary <a class="header-anchor" href="https://bitsofco.de/hiding-elements-with-css/">#</a></h2> <table> <thead> <tr> <th>&amp;nsp;</th> <th>display</th> <th>visibility</th> <th>opacity</th> <th>position</th> </tr> </thead> <tbody> <tr> <td>Is the box model generated?</td> <td>✗</td> <td>✓</td> <td>✓</td> <td>✓</td> </tr> <tr> <td>Does the box affect the document layout?</td> <td>✗</td> <td>✓</td> <td>✓</td> <td>✗</td> </tr> <tr> <td>Is the box visible on screen?</td> <td>✗</td> <td>✗</td> <td>✗</td> <td>✗</td> </tr> <tr> <td>Will the element’s content be read by screen readers?</td> <td>✗</td> <td>✗</td> <td>✓</td> <td>✓</td> </tr> <tr> <td>Is the element actionable (clickable, focusable)?</td> <td>✗</td> <td>✗</td> <td>✓</td> <td>✓</td> </tr> </tbody> </table> jQuery UI Events Cheatsheet 2015-07-14T00:00:00Z https://bitsofco.de/jquery-ui-events/ <p>While creating the <a href="https://bitsofco.de/accessible-multi-level-dropdown-navigation">accessible multi-level dropdown navigation</a>, I had to think a lot about which jQuery events were applicable for desktop vs mobile and mouse vs keyboard. I created a little cheatsheet for myself to reference, which turned out to be quite useful and so I thought I would share it.</p> <h2 id="the-ui-events" tabindex="-1">The UI Events <a class="header-anchor" href="https://bitsofco.de/jquery-ui-events/">#</a></h2> <p>The &quot;UI&quot; events I was looking to were events which invole the user interacting with an element. Here is an overview of the events -</p> <table> <thead> <tr> <th>Event</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>.focus</td> <td>When an actionable element, e.g. <code>a</code>, <code>input</code> or <code>button</code>, gains focus</td> </tr> <tr> <td>.blur</td> <td>When an actionable element loses focus</td> </tr> <tr> <td>.focusin</td> <td>When an actionable element, or its children, gains focus</td> </tr> <tr> <td>.focusout</td> <td>When an actionable element, or its children, loses focus</td> </tr> <tr> <td>.click</td> <td>When an element is clicked</td> </tr> <tr> <td>.dblclick</td> <td>When an element is double-clicked</td> </tr> <tr> <td>.mousedown</td> <td>When a pointer presses on an element</td> </tr> <tr> <td>.mouseup</td> <td>When a pointer releases press on an element</td> </tr> <tr> <td>.mouseenter</td> <td>When a pointer enters the area of an element</td> </tr> <tr> <td>.mouseleave</td> <td>When a pointer leaves the area of an element</td> </tr> <tr> <td>.mousemove</td> <td>When a pointer moves within the area of an element</td> </tr> <tr> <td>.mouseout</td> <td>When a pointer is registered outside the area of an element after previously being registered within it</td> </tr> <tr> <td>.mouseover</td> <td>When a pointer enters the area of an element</td> </tr> <tr> <td>.hover</td> <td>A shorthand for both .mouseenter and .mouseleave events</td> </tr> <tr> <td>.keydown</td> <td>When a key is pressed while on an element</td> </tr> <tr> <td>.keyup</td> <td>When a key is released while on an element</td> </tr> <tr> <td>.keypress</td> <td>When a key (not including modifier and non-printing keys such as Esc) is pressed while on an element</td> </tr> </tbody> </table> <h2 id="responding-user-interactions" tabindex="-1">Responding User Interactions <a class="header-anchor" href="https://bitsofco.de/jquery-ui-events/">#</a></h2> <p>The way these events are organised in the jQuery documentation can be a bit confusing. For example, the only events listed under <a href="https://api.jquery.com/category/events/keyboard-events/">keyboard events</a> are <code>.keydown</code>, <code>.keyup</code>, and <code>.keypress</code>. However, other events can also be triggered by the keyboard.</p> <p>Here is a list of the UI events and which types of user interaction each can be triggered by -</p> <table> <thead> <tr> <th>Event</th> <th>Mouse?</th> <th>Keyboard?</th> <th>Screen Tap?</th> </tr> </thead> <tbody> <tr> <td>.focus</td> <td>✓</td> <td>✓</td> <td>✗</td> </tr> <tr> <td>.blur</td> <td>✓</td> <td>✓</td> <td>✗</td> </tr> <tr> <td>.focusin</td> <td>✓</td> <td>✓</td> <td>✗</td> </tr> <tr> <td>.focusout</td> <td>✓</td> <td>✓</td> <td>✗</td> </tr> <tr> <td>.click</td> <td>✓</td> <td>✓</td> <td>✓</td> </tr> <tr> <td>.dblclick</td> <td>✓</td> <td>✗</td> <td>✗</td> </tr> <tr> <td>.mousedown</td> <td>✓</td> <td>✗</td> <td>✓</td> </tr> <tr> <td>.mouseup</td> <td>✓</td> <td>✗</td> <td>✓</td> </tr> <tr> <td>.mouseenter</td> <td>✓</td> <td>✗</td> <td>✓</td> </tr> <tr> <td>.mouseleave</td> <td>✓</td> <td>✗</td> <td>✓</td> </tr> <tr> <td>.mousemove</td> <td>✓</td> <td>✗</td> <td>✓</td> </tr> <tr> <td>.mouseout</td> <td>✓</td> <td>✗</td> <td>✓</td> </tr> <tr> <td>.mouseover</td> <td>✓</td> <td>✗</td> <td>✓</td> </tr> <tr> <td>.keydown</td> <td>✗</td> <td>✓</td> <td>✗</td> </tr> <tr> <td>.keyup</td> <td>✗</td> <td>✓</td> <td>✗</td> </tr> <tr> <td>.keypress</td> <td>✗</td> <td>✓</td> <td>✗</td> </tr> </tbody> </table> <h3 id="screen-tap-interactions" tabindex="-1">Screen Tap Interactions <a class="header-anchor" href="https://bitsofco.de/jquery-ui-events/">#</a></h3> <p>Screen taps are treated very similarly to clicks. Each screen tap is almost like each time the mouse moves on screen. This is why a doubleclick cannot be registered through tapping. However, because of this, we can get some events blocking the registering of other events.</p> <p>As a test, I chained all the events which can be triggered by a screen tap. When each event is registered, it is appended to a list on screen.</p> <pre><code>$('.test').on('click mousedown mouseup mouseenter mouseleave mousemove mouseout mouseover', function(event) { $('#output').append('&lt;li&gt;'+event.type+'&lt;/li&gt;'); return false; }) </code></pre> <p>When chaining all the events, the <code>click</code>, <code>mousedown</code>, and <code>mouseup</code> events are never registered.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/-EXaw8tmFL-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/-EXaw8tmFL-720.gif 720w"><img alt="Tap UI Events Registered with mousemove" loading="lazy" decoding="async" src="https://bitsofco.de/img/-EXaw8tmFL-720.webp" width="720" height="1060"></picture></p> <p>But when the <code>mousemove</code> event was removed, the other, previosuly unregistered, events show up -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/KmA2E4XR1K-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/KmA2E4XR1K-720.gif 720w"><img alt="Tap UI Events Registered without mousemove" loading="lazy" decoding="async" src="https://bitsofco.de/img/KmA2E4XR1K-720.webp" width="720" height="1116"></picture></p> <h3 id="keyboard-interactions" tabindex="-1">Keyboard Interactions <a class="header-anchor" href="https://bitsofco.de/jquery-ui-events/">#</a></h3> <p>When dealing with keyboard interactions, which key pressed becomes pertinent. Some events are triggered by the pressing of a particular key, while, with others, you can access the particular key that was pressed.</p> <table> <thead> <tr> <th>Event</th> <th>Which Key?</th> </tr> </thead> <tbody> <tr> <td>.focus</td> <td><code>tab</code> key released while on target element</td> </tr> <tr> <td>.blur</td> <td><code>tab</code> key pressed while on target element</td> </tr> <tr> <td>.focusin</td> <td><code>tab</code> key released while on target element or its children</td> </tr> <tr> <td>.focusout</td> <td><code>tab</code> key pressed while on target element or its children</td> </tr> <tr> <td>.click</td> <td><code>return</code> key pressed while on target element</td> </tr> <tr> <td>.keydown</td> <td>any key pressed while on target element</td> </tr> <tr> <td>.keyup</td> <td>any key released while on target element</td> </tr> <tr> <td>.keypress</td> <td>any key (not including modifier keys) pressed while on target element</td> </tr> </tbody> </table> <p>For the events which respond to any key, we can find out which key was pressed simply -</p> <pre><code>$('.element').on('keydown keyup keypress', function(e) { // log the code of the key pressed to the console console.log(e.keyCode); if ( e.keyCode === 9 ) { // tab key pressed } if ( e.keyCode === 13 ) { // return key pressed } }) </code></pre> <p>These are some of the nuances that it helps to be aware of when chaining multiple events and targetting different interactions. I hope you find this useful!</p> A More Accessible Multi-Level Dropdown Navigation 2015-07-07T00:00:00Z https://bitsofco.de/accessible-multi-level-dropdown-navigation/ <p>One of the biggest accessibility issues I see, and face myself, are with navigation menus, particularly multi-level drop-down menus. So this week, I decided to work on creating the most accessible multi-level dropdown menu I could.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/svFgPtYLN_-700.avif 700w"><source type="image/webp" srcset="https://bitsofco.de/img/svFgPtYLN_-700.webp 700w"><img alt="Accessible Multi-Level Dropdown Navigation Image" loading="lazy" decoding="async" src="https://bitsofco.de/img/svFgPtYLN_-700.png" width="700" height="427"></picture></p> <p><a href="https://ireade.github.io/accessible-dropdown-navigation/">Live Demo</a> / <a href="https://github.com/ireade/accessible-dropdown-navigation">Github project</a></p> <h2 id="aims" tabindex="-1">Aims <a class="header-anchor" href="https://bitsofco.de/accessible-multi-level-dropdown-navigation/">#</a></h2> <p>There were four things I was trying to achieve -</p> <h3 id="1-the-navigation-should-be-user-input-agnostic" tabindex="-1">1. The navigation should be user input agnostic <a class="header-anchor" href="https://bitsofco.de/accessible-multi-level-dropdown-navigation/">#</a></h3> <p>There are many different ways users can interact with websites, especially now with the introduction of touchscreen devices. So, I wanted the navigation to be able to respond to these four methods of user input -</p> <ul> <li>Using a mouse to <strong>hover</strong> over the element</li> <li><strong>Clicking</strong> on the element with a mouse</li> <li><strong>Tapping</strong> on the element on a touchscreen</li> <li><strong>Tabbing</strong> into and out of the element with a keyboard</li> </ul> <p>A lot of solutions I have seen work well with some of these methods of user input, but not necessarily the others. For example, the css-only dropdown menu doesn't work for users navigating with a keyboard. This is because it uses the <code>:focus</code> or <code>:hover</code> pseudo-class on the <code>li</code> rather than an actionable element.</p> <h3 id="2-these-methods-of-user-input-should-be-screen-size-agnostic" tabindex="-1">2. These methods of user input should be screen size agnostic. <a class="header-anchor" href="https://bitsofco.de/accessible-multi-level-dropdown-navigation/">#</a></h3> <p>It is no longer reliable to assume that a smaller viewport means that the user is on a tablet and tapping on the screen, or that larger viewports mean that the user is on a desktop and is accessing the site with a mouse or keyboard.</p> <p>Therefore, I wanted this menu to be able to respond to click events on smaller viewports, as well as tap events on larger viewports.</p> <h3 id="3-responsive-design" tabindex="-1">3. Responsive design <a class="header-anchor" href="https://bitsofco.de/accessible-multi-level-dropdown-navigation/">#</a></h3> <p>Because everything has to be responsive.</p> <h3 id="4-provide-some-fallback-for-users-without-javascript" tabindex="-1">4. Provide some fallback for users without javascript. <a class="header-anchor" href="https://bitsofco.de/accessible-multi-level-dropdown-navigation/">#</a></h3> <p>Supporting users without javascript can be a bit of a contentious issue. According to the <a href="https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/">UK Government Digital Service</a>, 1.1% of users (in the UK) do not have javascript enabled. Regardless of the debate, as my aim with this was to create something as accessible as possible, providing some fallback was necessary.</p> <h2 id="my-solution" tabindex="-1">My Solution <a class="header-anchor" href="https://bitsofco.de/accessible-multi-level-dropdown-navigation/">#</a></h2> <p>This is what it looks like going through all the levels of the navigation using only the keyboard -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/atNoxKLkye-720.avif 720w"><source type="image/gif" srcset="https://bitsofco.de/img/atNoxKLkye-720.gif 720w"><img alt="Accessible Multi-Level Dropdown Navigation Image Animated" loading="lazy" decoding="async" src="https://bitsofco.de/img/atNoxKLkye-720.webp" width="720" height="453"></picture></p> <h3 id="the-markup" tabindex="-1">The Markup <a class="header-anchor" href="https://bitsofco.de/accessible-multi-level-dropdown-navigation/">#</a></h3> <p>The HTML follows the standard structure for drop-down navigations -</p> <pre><code>&lt;nav id=&quot;nav&quot;&gt; &lt;ul&gt; &lt;!-- Menu tooggle on smaller screens --&gt; &lt;li id=&quot;toggleMenu&quot;&gt; &lt;a href=&quot;/sitemap.html&quot;&gt; &lt;i class=&quot;fa fa-bars&quot;&gt;&lt;/i&gt; &lt;i class=&quot;fa fa-times&quot;&gt;&lt;/i&gt; &lt;!-- As icons are being used to represent open/close, provide hidden text for screen readers --&gt; &lt;span id=&quot;toggleMenu-text&quot;&gt;Toggle Navigation&lt;/span&gt; &lt;/a&gt; &lt;/li&gt; &lt;!-- Simple menu item without sub menu --&gt; &lt;li&gt;&lt;a href=&quot;/&quot;&gt;Level 1&lt;/a&gt;&lt;/li&gt; &lt;!-- Menu item with one sub menu --&gt; &lt;li&gt; &lt;a href=&quot;/sitemap.html&quot;&gt;Level 2&lt;/a&gt; &lt;!-- sub menu (level 2) --&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;/&quot;&gt;Example Link&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;/&quot;&gt;Example Link&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;!-- Menu item with two levels of sub menu --&gt; &lt;li&gt; &lt;a href=&quot;/sitemap.html&quot;&gt;Level 3&lt;/a&gt; &lt;!-- sub menu (level 2) --&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;/&quot;&gt;Example Link&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;/&quot;&gt;Example Link&lt;/a&gt;&lt;/li&gt; &lt;li&gt; &lt;a href=&quot;/&quot;&gt;Example Link&lt;/a&gt; &lt;!-- sub menu (level 3) --&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;/&quot;&gt;Example Link&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;/&quot;&gt;Example Link&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;/&quot;&gt;Example Link&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; &lt;/ul&gt; &lt;/nav&gt; </code></pre> <h3 id="the-javascript" tabindex="-1">The JavaScript <a class="header-anchor" href="https://bitsofco.de/accessible-multi-level-dropdown-navigation/">#</a></h3> <p>The script was organised into three sections. First, the section controlling what happens when users interact with a list item that has a sub menu -</p> <pre><code>// When interacting with a li that has a sub menu $('li:has(&quot;ul&quot;)').on('mouseover keyup click mouseleave', function(e) { // If either - // tabbing into the li that has a sub menu // hovering over the li that has a sub menu if ( e.keyCode === 9 | e.type === 'mouseover' ) { // Show sub menu $(this).children('ul').removeClass('js-hideElement'); $(this).children('ul').addClass('js-showElement'); } // If mouse leaves li that has sub menu if ( e.type === 'mouseleave' ) { // hide sub menu $(this).children('ul').removeClass('js-showElement'); $(this).children('ul').addClass('js-hideElement'); } // If clicking on li that has a sub menu if ( e.type === 'click' ) { // If sub menu is already open if ( $(this).children('a').hasClass('js-openSubMenu') ) { // remove Open class $(this).children('a').removeClass('js-openSubMenu'); // Hide sub menu $(this).children('ul').removeClass('js-showElement'); $(this).children('ul').addClass('js-hideElement'); // If sub menu is closed } else { // add Open class $(this).children('a').addClass('js-openSubMenu'); // Show sub menu $(this).children('ul').removeClass('js-hideElement'); $(this).children('ul').addClass('js-showElement'); } return false; } // end click event }); </code></pre> <p>Second, the section controlling the opening and closing of sub menus when users navigate using a keyboard -</p> <pre><code> // If key is pressed while on the last link in a sub menu $('li &gt; ul &gt; li:last-child &gt; a').on('keydown', function(e) { // If tabbing out of the last link in a sub menu AND NOT tabbing into another sub menu if ( (e.keyCode == 9) &amp;&amp; $(this).parent('li').children('ul').length == 0 ) { // Close this sub menu $(this).parent('li').parent('ul').removeClass('js-showElement'); $(this).parent('li').parent('ul').addClass('js-hideElement'); // If also tabbing out of a third level sub menu // AND there are no other links in the parent (level 2) sub menu if ( $(this).parent('li').parent('ul').parent('li').parent('ul').parent('li').children('ul').length &gt; 0 &amp;&amp; $(this).parent('li').parent('ul').parent('li').is(':last-child') ) { // Close the parent sub menu (level 2) as well $(this).parent('li').parent('ul').parent('li').parent('ul').removeClass('js-showElement'); $(this).parent('li').parent('ul').parent('li').parent('ul').addClass('js-hideElement'); } } }) </code></pre> <p>Finally, the section controlling the menu toggle for smaller screens, adding the plus mark for list items that have a sub menu, and removing the <code>no-js</code> class on the <code>html</code> element -</p> <pre><code>// toggle menu on smaller screens $('#toggleMenu').on('click', function() { if ( $(this).hasClass('js-open') ) { $('#nav &gt; ul &gt; li:not(#toggleMenu)').removeClass('js-showElement'); $(this).removeClass('js-open'); } else { $('#nav &gt; ul &gt; li:not(#toggleMenu)').addClass('js-showElement'); $(this).addClass('js-open'); } return false; }) // Add plus mark to li that have a sub menu $('li:has(&quot;ul&quot;) &gt; a').append('&lt;span class=&quot;plusMark&quot;&gt;+&lt;/span&gt;') // Remove no-js class $('html').removeClass('no-js'); </code></pre> <h3 id="no-js-fallback" tabindex="-1">No-JS Fallback <a class="header-anchor" href="https://bitsofco.de/accessible-multi-level-dropdown-navigation/">#</a></h3> <p>For users without JavaScript enabled, I provided two fallbacks -</p> <p><strong>1. For users able to use a mouse or tap on screen, I used the css only dropdown-navigation</strong>. This solution uses the adjacent sibling selector (<code>+</code>) plus the <code>:hover</code> and <code>:focus</code> pseudo-classes to hide and reveal the sub menus.</p> <pre><code>html.no-js li:hover &gt; a + ul { display: block; } @media screen and (max-width: 650px) { html.no-js #nav:hover &gt; ul &gt; li:not(#toggleMenu) { display: block; } html.no-js #nav:hover li:hover &gt; a + ul { display: block; } } </code></pre> <p><strong>2. For users using a keyboard, provide a link to another page with a full site map</strong>. Although this may be an edge case, it is simple to provide this as an absolute fallback.</p> <pre><code>&lt;!-- Menu item with one sub menu --&gt; &lt;li&gt; &lt;a href=&quot;/sitemap.html#level2&quot;&gt;Level 2&lt;/a&gt; &lt;!-- sub menu (level 2) --&gt; &lt;ul&gt; &lt;li&gt;&lt;a href=&quot;/&quot;&gt;Example Link&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href=&quot;/&quot;&gt;Example Link&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;/li&gt; </code></pre> <p>That's it! This actually took a lot longer than I thought it would, trying to deal with the many different type of user interaction, and I'm sure there are improvements I can make. If you have any comments or feedback, leave a comment below.</p> Controlling The Box Model 2015-06-30T00:00:00Z https://bitsofco.de/controlling-the-box-model/ <p><a href="https://www.w3.org/TR/CSS2/box.html">Every element in the document tree is is a rectangular box</a>. The CSS box model is what describes these boxes and their components.</p> <h2 id="box-model-basics" tabindex="-1">Box Model Basics <a class="header-anchor" href="https://bitsofco.de/controlling-the-box-model/">#</a></h2> <p>Each rectangular box in an HTML document is made up of four areas - the content area, padding area, border area, and margin area. The perimeter of each of these areas is called it's &quot;edge&quot;.</p> <p>Consider this example -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>box<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token comment">&lt;!-- start content area --></span><br> Lorem ipsum dolor sit amet.<br> <span class="token comment">&lt;!-- end content area --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br></code></pre> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.box</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 300px<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 300px<span class="token punctuation">;</span><br><br> <span class="token property">padding</span><span class="token punctuation">:</span> 50px<span class="token punctuation">;</span> <span class="token comment">/* padding area */</span><br> <span class="token property">margin</span><span class="token punctuation">:</span> 50px<span class="token punctuation">;</span> <span class="token comment">/* margin area */</span><br> <span class="token property">border</span><span class="token punctuation">:</span> 50px solid grey<span class="token punctuation">;</span> <span class="token comment">/* border area */</span><br><span class="token punctuation">}</span></code></pre> <h3 id="content-area" tabindex="-1">Content Area <a class="header-anchor" href="https://bitsofco.de/controlling-the-box-model/">#</a></h3> <p>The content area is the area taken up by the actual content of the element. This can be the text, image or other media. It's edge is called the &quot;content edge&quot; or &quot;inner edge&quot;.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/HuFyhclkT7-1065.avif 1065w"><source type="image/webp" srcset="https://bitsofco.de/img/HuFyhclkT7-1065.webp 1065w"><img alt="Content Area and Content Edge" loading="lazy" decoding="async" src="https://bitsofco.de/img/HuFyhclkT7-1065.jpeg" width="1065" height="650"></picture></p> <h3 id="padding-area" tabindex="-1">Padding Area <a class="header-anchor" href="https://bitsofco.de/controlling-the-box-model/">#</a></h3> <p>The padding area is the space we specify between the content and the border. It's edge is called the &quot;padding edge&quot;.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/zYTAvtCw0D-1065.avif 1065w"><source type="image/webp" srcset="https://bitsofco.de/img/zYTAvtCw0D-1065.webp 1065w"><img alt="Padding Area and Padding Edge" loading="lazy" decoding="async" src="https://bitsofco.de/img/zYTAvtCw0D-1065.jpeg" width="1065" height="650"></picture></p> <h3 id="border-area" tabindex="-1">Border Area <a class="header-anchor" href="https://bitsofco.de/controlling-the-box-model/">#</a></h3> <p>The border area is area taken up by the thickness of the border. Its edge is called the &quot;border edge&quot;.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/P1PGsQ7PDm-1065.avif 1065w"><source type="image/webp" srcset="https://bitsofco.de/img/P1PGsQ7PDm-1065.webp 1065w"><img alt="Border Area and Border Edge" loading="lazy" decoding="async" src="https://bitsofco.de/img/P1PGsQ7PDm-1065.jpeg" width="1065" height="650"></picture></p> <h3 id="margin-area" tabindex="-1">Margin Area <a class="header-anchor" href="https://bitsofco.de/controlling-the-box-model/">#</a></h3> <p>The margin area is the space outside the border. Although not strictly part of the element itself, it is included when we consider how much space on the page is taken up by the element. It's edge is called the &quot;margin edge&quot; or the &quot;outer edge&quot;.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/o_E9Ks4qi_-1065.avif 1065w"><source type="image/webp" srcset="https://bitsofco.de/img/o_E9Ks4qi_-1065.webp 1065w"><img alt="Margin Area and Margin Edge" loading="lazy" decoding="async" src="https://bitsofco.de/img/o_E9Ks4qi_-1065.jpeg" width="1065" height="698"></picture></p> <p>For block level elements, we are able to control the height and width of these rectangular boxes with the <code>height</code> and <code>width</code> css properties. However, given these disparate areas of each element, we have to specify what we mean by the&quot; &quot;width&quot; or &quot;height&quot; of the element. This is where the box-sizing property comes in.</p> <h2 id="the-box-sizing-property" tabindex="-1">The Box-Sizing Property <a class="header-anchor" href="https://bitsofco.de/controlling-the-box-model/">#</a></h2> <p>The <code>box-sizing</code> property defines which of the four edges we use to determine the width or height of an element. Depending on which area we specify, the content area may be adjusted accordingly.</p> <p>The box-sizing property accepts four values - <code>content-box</code>, <code>padding-box</code>, <code>border-box</code>, and <code>inherit</code>. To highlight the difference between these, lets apply each of them to our <code>.box</code> example.</p> <h3 id="content-box" tabindex="-1">content-box <a class="header-anchor" href="https://bitsofco.de/controlling-the-box-model/">#</a></h3> <blockquote> <p>width = content area</p> </blockquote> <p>This is the default value. It sets the width of the element, in our case 300px, to be equal to the width of the content area.The thickness of the padding and border, therefore make the element wider than the 300px. The element width, in the more commonplace sense of the word, is now actually 500px, and the total amount of space taken up by the element is 600px.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/3dVcDrDWuy-1056.avif 1056w"><source type="image/webp" srcset="https://bitsofco.de/img/3dVcDrDWuy-1056.webp 1056w"><img alt="Box Sizing Content-Box" loading="lazy" decoding="async" src="https://bitsofco.de/img/3dVcDrDWuy-1056.jpeg" width="1056" height="648"></picture></p> <h3 id="padding-box" tabindex="-1">padding-box <a class="header-anchor" href="https://bitsofco.de/controlling-the-box-model/">#</a></h3> <blockquote> <p>width = content area + padding area</p> </blockquote> <p>This value includes the thickness of the padding in the calculation of the element width. The content area size is adjusted to a width of 200px to allow for this.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/pXhKFQRbdC-1056.avif 1056w"><source type="image/webp" srcset="https://bitsofco.de/img/pXhKFQRbdC-1056.webp 1056w"><img alt="Box Sizing Padding-Box" loading="lazy" decoding="async" src="https://bitsofco.de/img/pXhKFQRbdC-1056.jpeg" width="1056" height="648"></picture></p> <p><em>Note</em>: This value is only supported in the Firefox browser. Other browsers will use the default, content-box if this value is set.</p> <h3 id="border-box" tabindex="-1">border-box <a class="header-anchor" href="https://bitsofco.de/controlling-the-box-model/">#</a></h3> <blockquote> <p>width = content area + padding area + border area</p> </blockquote> <p>This value includes both the padding and border size in the calculation of the element width. In this example, the width of the actual content area is now only 100px.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/DJ9STXU4oU-1056.avif 1056w"><source type="image/webp" srcset="https://bitsofco.de/img/DJ9STXU4oU-1056.webp 1056w"><img alt="Box Sizing Border-Box" loading="lazy" decoding="async" src="https://bitsofco.de/img/DJ9STXU4oU-1056.jpeg" width="1056" height="648"></picture></p> <h3 id="inherit" tabindex="-1">inherit <a class="header-anchor" href="https://bitsofco.de/controlling-the-box-model/">#</a></h3> <p>This value sets the box-sizing to whichever value the parent element's box-sizing is set to.</p> <h2 id="controlling-the-box-model" tabindex="-1">Controlling The Box Model <a class="header-anchor" href="https://bitsofco.de/controlling-the-box-model/">#</a></h2> <p>Which box sizing method you use really depends on what you prefer to think of has the element. For most people, and myself, using the <code>border-box</code> value is the most logical way of defining the element sizes.</p> <p>To override the default content-box value, I always include this box sizing reset recommended by <a href="https://www.paulirish.com/2012/box-sizing-border-box-ftw/">Paul Irish</a> in my reset.css file -</p> <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 comment">/* Set border-box as the global default */</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, *:after</span> <span class="token punctuation">{</span><br> <span class="token comment">/* Allow each element to override */</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> Pseudo-Classes, Pseudo-Elements, and Colon Notation 2015-06-23T00:00:00Z https://bitsofco.de/pseudo-classes-pseudo-elements-and-colon-notation/ <p>In my post about <a href="https://bitsofco.de/5-lesser-used-css-selectors">5 lesser used CSS selectors</a>, I used a single colon when referring to the first-letter and first-line pseudo-elements. It was pointed out to me that I should have used the double colon notation for selectors because they were pseudo-elements.</p> <p>So, in this article, I explore the difference between pseudo-elements and pseudo-classes and which colon syntax should be used.</p> <h2 id="pseudo-classes-vs-pseudo-elements" tabindex="-1">Pseudo-Classes vs Pseudo-Elements <a class="header-anchor" href="https://bitsofco.de/pseudo-classes-pseudo-elements-and-colon-notation/">#</a></h2> <p>Pseudo-classes and pseudo-elements are types of css selectors that allow us to select elements which are not normally available through the DOM. There are currently only 4 psuedo-elements, and 23 pseudo-classes -</p> <table> <thead> <tr> <th>Pseudo-Classes</th> <th>Pseudo-Elements</th> </tr> </thead> <tbody> <tr> <td><code>E:root</code>, <code>E:nth-child(n)</code>, <code>E:nth-last-child(n)</code>, <code>E:nth-of-type(n)</code>, <code>E:nth-last-of-type(n)</code>, <code>E:first-child</code>, <code>E:last-child</code>, <code>E:first-of-type</code>, <code>E:last-of-type</code>, <code>E:only-child</code>, <code>E:only-of-type</code>, <code>E:empty</code>, <code>E:link</code>, <code>E:visited</code>, <code>E:active</code>, <code>E:hover</code>, <code>E:focus</code>, <code>E:target</code>, <code>E:lang(x)</code>, <code>E:enabled</code>, <code>E:disabled</code>, <code>E:checked</code>, <code>E:not(s)</code></td> <td><code>E::first-line</code>, <code>E::first-letter</code>, <code>E::before</code>, <code>E::after</code></td> </tr> </tbody> </table> <h3 id="pseudo-classes" tabindex="-1">Pseudo-classes <a class="header-anchor" href="https://bitsofco.de/pseudo-classes-pseudo-elements-and-colon-notation/">#</a></h3> <p><a href="https://www.w3.org/TR/css3-selectors/#pseudo-classes">Pseudo-classes</a>, according to the W3C Recommendation, provide a way for us to select elements &quot;based on information that lies outside of the document tree <em>or</em> that cannot be expressed using the other simple selectors&quot;.</p> <p>This covers two cases. First, <strong>the selection of information based on information that lies outside the document tree</strong>. An example of this is the <code>:visited</code> pseudo-class.</p> <pre><code>a:visited { // styles } </code></pre> <p>Use of this pseudo-class is dependent on information about the link that is not accessible through the DOM. There is no way, in HTML or JavaScript, to determine if a link has been visited.</p> <p>The second case is <strong>the selection of elements that cannot be expressed using other simple selectors</strong>. An example of this is <code>:nth-child(n)</code>.</p> <pre><code>.list-item:nth-child(6) { // styles } </code></pre> <p>Although we may have the class name of the the specific element we want, there is no other way to select an item based on its position amongst its siblings.</p> <blockquote> <p>Psuedo-classes select elements that already exist in the DOM, but which are under conditions that are not accessible through the DOM or through other simple selectors</p> </blockquote> <h3 id="pseudo-elements" tabindex="-1">Pseudo-elements <a class="header-anchor" href="https://bitsofco.de/pseudo-classes-pseudo-elements-and-colon-notation/">#</a></h3> <p><a href="https://www.w3.org/TR/css3-selectors/#pseudo-elements">Pseudo-elements</a>, on the other hand, &quot;create abstractions about the document tree beyond those specified by the document language&quot;.</p> <p>In other words, <strong>using pseudo-element selectors essentially creates a new virtual element</strong> that can be manipulated as though it were its own element. These new virtual elements do not exist independently, but rather belong to another existing element, called their originating element.</p> <p>For example, with the <code>::after</code> pseudo-element, we can generate new content that does not actually exist in the DOM. Using the example I gave two weeks ago -</p> <pre><code>*:lang(de)::before { content: &quot;DE&quot;; position: absolute; top: 0; left: -55px; width: 40px; height: 40px; text-align: center; padding: 10px 0; border-radius: 50%; background-color: #FFCE00; } </code></pre> <p>With the <code>::first-line</code> and <code>::first-letter</code> selectors, although the content does already exist, using the selector allows us to target the content in a way that is not normally accessible and treat the content as though it were its own element.</p> <pre><code>article::first-letter { font-size: 8rem; line-height: 1; float: left; padding-right: 1rem; margin-top: -2rem; } </code></pre> <blockquote> <p>Pseudo-elements create a virtual element which lies outside the document tree, either with content that already exists, or by creating new content entirely.</p> </blockquote> <p>The difference between pseudo-classes and pseudo-elements can be summarised as -</p> <blockquote> <p>With pseudo-classes, it is the <strong>information</strong> used to the select the element that lies outside the document tree. With pseudo-elements, it is the <strong>element itself</strong> that lies outside the document tree.</p> </blockquote> <h2 id="colon-notation-single-or-double" tabindex="-1">Colon Notation: Single or Double? <a class="header-anchor" href="https://bitsofco.de/pseudo-classes-pseudo-elements-and-colon-notation/">#</a></h2> <p>In CSS2, both pseudo-classes and pseudo-elements used single colon notation. In CSS3, to make a distinction between the two types of selectors, it was introduced that pseudo-elements use a double colon instead.</p> <pre><code>foo:pseudo-class bar::pseudo-element </code></pre> <p>In the <a href="https://dev.w3.org/csswg/selectors-4/#pseudo-elements">Editor's Draft for CSS4 selectors</a>, it is specifically stated -</p> <blockquote> <p>&quot;Authors must always use the double-colon syntax for these pseudo-elements&quot;</p> </blockquote> <p>However, the problem is that Internet Explorer 8, which according to caniuse.com currently accounts for <a href="https://caniuse.com/#search=after">2.19% of global browser usage</a>, <strong>does not support the double colon notation</strong>. It only accepts a single colon for both pseudo-classes and pseudo-elements.</p> <p>Because of this, <strong>all modern browsers are required to accept the single colon syntax</strong> for the current 4 pseudo-elements mentioned (but not for any future pseudo-elements introduced).</p> <p>So which version should we be using? At this point, and depending on our target audience, IE 8 usage is large enough that we may still need to support it and therefore use the single colon syntax. However, <strong>the default should be to use the double colons wherever possible for future proofing</strong>.</p> Designer News Clone (Part 2) - Implementing Firebase Security 2015-06-16T00:00:00Z https://bitsofco.de/designer-news-clone-part-2-implementing-firebase-security/ <p>A few weeks ago, I wrote about <a href="https://bitsofco.de/building-a-designer-news-clone">how I built a Designer News clone with AngularJS and Firebase</a>. A lot of the feedback I received was about how the application could be secure because it is written purely with front-end code. So this week, I tried to implement <a href="https://www.firebase.com/docs/security/">Firebase's built-in security rules</a>.</p> <p>Now, I'm not a back-end developer. A lot of this was brand new to me, but after spending days going over the documentation, I was able to successfully add security rules to my application. What I came up with may not be the best way to do it, but it worked, and that's at least half the battle!</p> <h2 id="introduction-to-firebase-security" tabindex="-1">Introduction to Firebase Security <a class="header-anchor" href="https://bitsofco.de/designer-news-clone-part-2-implementing-firebase-security/">#</a></h2> <h3 id="format" tabindex="-1">Format <a class="header-anchor" href="https://bitsofco.de/designer-news-clone-part-2-implementing-firebase-security/">#</a></h3> <p>Firebase security rules are written in json format, and are structured to mirror your data. So, for example, if your data is structured like this -</p> <pre><code>stories: { story1: { title: &quot;Story 1 Title Here&quot;, description: &quot;Story 1 Description Here&quot; }, story2: { title: &quot;Story 2 Title Here&quot;, description: &quot;Story 2 Description Here&quot; } } </code></pre> <p>The security rules will be structured like this -</p> <pre><code>{ &quot;rules&quot;: { &quot;stories&quot;: { &quot;$story_id&quot;: { // rules go here &quot;title&quot;: { // more rules go here }, &quot;description&quot;: { // more rules go here } } } } } </code></pre> <h3 id="rule-types" tabindex="-1">Rule Types <a class="header-anchor" href="https://bitsofco.de/designer-news-clone-part-2-implementing-firebase-security/">#</a></h3> <p>There are three types of rules you can apply to each of the nodes -</p> <table> <thead> <tr> <th>Rule Type</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>.read</code></td> <td>Describes if and when data is allowed to be read by users.</td> </tr> <tr> <td><code>.write</code></td> <td>Describes if and when data is allowed to be written.</td> </tr> <tr> <td><code>.validate</code></td> <td>Defines what a correctly formatted value will look like, whether it has child attributes, and the data type.</td> </tr> </tbody> </table> <p>(taken form <a href="https://www.firebase.com/docs/security/quickstart.html">Firebase docs</a> )</p> <p>To apply these rule types, you need to have a corresponding statement which must resolve as true. For example -</p> <pre><code>{ &quot;rules&quot;: { &quot;foo&quot;: { // foo can be read by anyone &quot;.read&quot;: true, // data can only be written to foo if the user is logged in &quot;.write&quot;: &quot;auth !== null&quot;, // new data being written to foo must be a string &quot;.validate&quot;: &quot;newData.isString()&quot; } } } </code></pre> <p>You can find the list of methods used to create these statements in the <a href="https://www.firebase.com/docs/security/api/">Firebase API Reference</a>.</p> <h3 id="rules-cascade" tabindex="-1">Rules Cascade <a class="header-anchor" href="https://bitsofco.de/designer-news-clone-part-2-implementing-firebase-security/">#</a></h3> <p>A critical concept about the way the security rules work is the rules cascade. As the Firebase docs explain -</p> <blockquote> <p>The child rules can only grant additional privileges to what parent nodes have already declared. They cannot revoke a read or write privilege.</p> </blockquote> <p>This means that if you set a <code>.read</code> rule for a parent node, then that rule applies to all child nodes. If you declare another <code>.read</code> rule in the child node, it will be ignored.</p> <pre><code>{ &quot;rules&quot;: { &quot;foo&quot;: { // foo can be read by anyone &quot;.read&quot;: true, &quot;bar&quot;: { // ignored because read was already allowed // so bar can also be read by anyone &quot;.read&quot;: false } } } } </code></pre> <h3 id="referincing-data" tabindex="-1">Referincing Data <a class="header-anchor" href="https://bitsofco.de/designer-news-clone-part-2-implementing-firebase-security/">#</a></h3> <p>When referencing the data under the current node, a distinction is made between <code>data</code> and <code>newData</code> -</p> <table> <thead> <tr> <th>Rule Type</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>.read</code></td> <td>Describes if and when data is allowed to be read by users.</td> </tr> <tr> <td><code>.write</code></td> <td>Describes if and when data is allowed to be written.</td> </tr> <tr> <td><code>.validate</code></td> <td>Defines what a correctly formatted value will look like, whether it has child attributes, and the data type.</td> </tr> </tbody> </table> <p>To refer to data from other nodes, you can use one of the following -</p> <table> <thead> <tr> <th>Type</th> <th>Description</th> <th>Example</th> </tr> </thead> <tbody> <tr> <td><code>parent()</code></td> <td>The parent of the current node</td> <td><code>data.parent().child('sibling')</code></td> </tr> <tr> <td><code>child(childPath)</code></td> <td>The child of the current node</td> <td><code>data.child('title').val().isString()</code></td> </tr> <tr> <td><code>root</code></td> <td>The current data at the root of your Firebase</td> <td><code>root.child('foo').val()</code></td> </tr> </tbody> </table> <h2 id="implementing-security-to-the-designer-news-clone" tabindex="-1">Implementing Security to the Designer News Clone <a class="header-anchor" href="https://bitsofco.de/designer-news-clone-part-2-implementing-firebase-security/">#</a></h2> <p>Because of the rules cascade issue, I had to restructure my data a bit. Here is how it is now organised -</p> <pre><code>stories: { story1_id: { title: date: url: user: { first_name: last_name: title: id: } voteCount voters commentCount comments: { [array of commments] } } } users: { user1_id: { first_name: last_name: title: uid: karma: posts: { [array of posts] } comments: { [array of comments] } } } karma: { user1_id: [number], user2_id: [number], } </code></pre> <p>The main things I changed were -</p> <ul> <li>For each user in the users array, I set the Firebase key to be equal to the authentication ID</li> <li>Removed the karma from each individual user object, and moved it into its 'karma' own array</li> </ul> <p>Based on the way my data is now structures, here is how my rules file looks -</p> <pre><code>{ &quot;rules&quot;: { // Anyone can read any data &quot;.read&quot;: true, &quot;stories&quot;: { // Each story &quot;$story_id&quot;: { // Only logged in users can create a story &quot;.write&quot;: &quot;auth !== null&quot;, &quot;title&quot;: { // Validate only if the data doesn't already exist (so data cannot be edited) and if the new data is a string &quot;.validate&quot;: &quot;!data.exists() &amp;&amp; newData.isString()&quot; }, &quot;date&quot;: { // Validate only if the data doesn't already exist, and the data must be equal to the current datetime &quot;.validate&quot;: &quot;!data.exists() &amp;&amp; newData.val() &lt;= now&quot; }, &quot;url&quot;: { &quot;.validate&quot;: &quot;!data.exists() &amp;&amp; newData.isString()&quot; }, &quot;description&quot;: { &quot;.validate&quot;: &quot;!data.exists() &amp;&amp; newData.isString()&quot; }, &quot;user&quot;: { // Validate only if the data doesn't already exist, and the data must have the specified children &quot;.validate&quot;: &quot;!data.exists() &amp;&amp; newData.hasChildren(['first_name', 'last_name', 'title', 'id'])&quot; }, &quot;voteCount&quot;: { // Data can only be validated to this node if - // The data is new and the value is equal to 0 OR // The data already exists, the new value is equal to the old value plus 1, and the user making this addition is not the author of the story &quot;.validate&quot;: &quot;( !data.exists() &amp;&amp; newData.val() === 0 ) || ( data.exists() &amp;&amp; newData.val() === data.val() + 1 &amp;&amp; auth.uid !== root.child('stories').child($story_id).child('user').child('id').val() )&quot; }, &quot;voters&quot;: { &quot;.validate&quot;: true }, &quot;commentCount&quot;: { &quot;.validate&quot;: true }, &quot;comments&quot;: { &quot;$comment_id&quot;: { &quot;voteCount&quot;: { // Data can only be validated to this node if - // The data is new and the value is equal to 0 OR // If data already exists, the new value is equal to the old value plus 1, and the user making this addition is not the author of the comment &quot;.validate&quot;: &quot;( !data.exists() &amp;&amp; newData.val() === 0 ) || ( data.exists() &amp;&amp; newData.val() === data.val() + 1 &amp;&amp; auth.uid !== root.child('stories').child($story_id).child('comments').child($comment_id).child('user').child('id').val() )&quot; } } } } }, &quot;users&quot;: { // Each user &quot;$user_id&quot;: { // Data can only be written if - // The user doesn't already exist OR // The user does exist and is the same as the current authenticated user &quot;.write&quot;: &quot;!data.exists() || ( data.exists() &amp;&amp; auth.uid === $user_id )&quot;, &quot;uid&quot;: { // Data can only be validated to this node if - // The data doesn't already exist OR // The new data is equal to the old data, i.e. the data not be changed once set &quot;.validate&quot;: &quot;!data.exists() || data.val() === newData.val()&quot; }, &quot;first_name&quot;: { // Data can only be validated to this node if - // The new data is a string OR // No new data is incoming, i.e. the data is being deleted &quot;.validate&quot;: &quot;newData.isString() || !newData.exists()&quot; }, &quot;last_name&quot;: { &quot;.validate&quot;: &quot;newData.isString() || !newData.exists()&quot; }, &quot;title&quot;: { &quot;.validate&quot;: &quot;newData.isString() || !newData.exists()&quot; } } }, &quot;karma&quot;: { // Each user's karma &quot;$user_id&quot;: { // Data can only be written if - // The user doesnt already exist OR // No new data is incoming, i.e. the data is being deleted OR // The current authenticated user adding to this user's karma is not the same as this user, i.e. you can't add to your own karma &quot;.write&quot;: &quot;!data.exists() || !newData.exists() || ( auth !== null &amp;&amp; auth.uid !== $user_id )&quot;, // Data can only be validated to this node if The // The data doesn't already exist OR // Data does exist and the new karma is equal to the old value plus 1 &quot;.validate&quot;: &quot;!data.exists() || ( data.exists() &amp;&amp; newData.val() === data.val() + 1 )&quot; } } } } </code></pre> <p>That's it! One thing I am still struggling with is out to make sure that a user can only vote once. As far as I know, there is no way to loop through an array of data in the Firebase security methods to check if specific data exists.</p> <p>If you know of a way to do this, or if you have any other feedback, leave a comment below.</p> 5 Lesser Used CSS Selectors 2015-06-09T00:00:00Z https://bitsofco.de/5-lesser-used-css-selectors/ <p>If you are new to CSS, the selectors you use are probably limited to class names, IDs and generic elements. In fact, there are in total <a href="https://www.w3.org/TR/css-2010/#selectors">38 css selectors</a>, varying greatly in complexity.</p> <p>Here are 5 css selectors you may not have known about, and some use cases for them.</p> <table> <thead> <tr> <th>Selector</th> <th>Type</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>foo:empty</td> <td>structural pseudo-class</td> <td>a foo element that has no children</td> </tr> <tr> <td>foo::first-letter &amp;foo::first-line</td> <td>pseudo-element</td> <td>the first formatted letter &amp; line of the foo element</td> </tr> <tr> <td>foo:not(x)</td> <td>pseudo-class</td> <td>all foo elements except those with the attribute x</td> </tr> <tr> <td>foo:lang(de)</td> <td>pseudo-class</td> <td>a foo element in the German language</td> </tr> <tr> <td>foo:target</td> <td>pseudo-class</td> <td>a foo element being the target of a referring URI</td> </tr> </tbody> </table> <h2 id="empty" tabindex="-1">:empty <a class="header-anchor" href="https://bitsofco.de/5-lesser-used-css-selectors/">#</a></h2> <p>The :empty pseudo-class represents an element that has no children at all. This includes whitespace, text nodes, or an empty child element, but does not include html comments. For example -</p> <pre><code>&lt;!-- These will satisfy as empty --&gt; &lt;div&gt;&lt;/div&gt; &lt;div&gt;&lt;!-- comment --&gt;&lt;/div&gt; &lt;!-- These will NOT satisfy as empty --&gt; &lt;div&gt;foo&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;&lt;span&gt;&lt;/span&gt;&lt;/div&gt; &lt;div&gt; &lt;!-- comment --&gt; &lt;/div&gt; </code></pre> <p>The :empty pseudo-class is useful for elements which are styled in such a way that they appear regardless of whether they have content in them or not.</p> <p>An example could be alert message boxes. We only want them to appear under certain circumstances, and the text content within them may be dynamically inserted and removed depending on the context. Using the :empty pseudo-class, we can make sure that the box will be hidden when there is no content within it.</p> <pre><code>.alert { background-color: beige; border: 2px solid rgb(150, 150, 150); border-radius: 5px; padding: 5px 10px; display: inline-block; } .alert:empty { display: none; } </code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/VlK4Yz7pag-700.avif 700w"><source type="image/webp" srcset="https://bitsofco.de/img/VlK4Yz7pag-700.webp 700w"><img alt="Alert Boxes With and Without Using the Empty Selector" loading="lazy" decoding="async" src="https://bitsofco.de/img/VlK4Yz7pag-700.png" width="700" height="248"></picture></p> <h2 id="first-letter-and-first-line" tabindex="-1">::first-letter and ::first-line <a class="header-anchor" href="https://bitsofco.de/5-lesser-used-css-selectors/">#</a></h2> <p>The ::first-letter and ::first-line pseudo-elements represent the first letter and first line of the relevant element. What is great about ::first-line in particular is that it is responsive. As you shrink and enlarge the viewport and the text on the first line increases and decreases, the formatting is applied.</p> <p>As you would expect, they can be particularly useful when formatting articles. For example -</p> <pre><code>article::first-letter { font-size: 8rem; line-height: 1; float: left; padding-right: 1rem; margin-top: -2rem; } article::first-line { font-size: 3rem; } </code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/BFeYzaKOPK-700.avif 700w"><source type="image/webp" srcset="https://bitsofco.de/img/BFeYzaKOPK-700.webp 700w"><img alt="Article styled using first-letter and first-line" loading="lazy" decoding="async" src="https://bitsofco.de/img/BFeYzaKOPK-700.png" width="700" height="422"></picture></p> <h2 id="not-x" tabindex="-1">:not(x) <a class="header-anchor" href="https://bitsofco.de/5-lesser-used-css-selectors/">#</a></h2> <p>The :not pseudo-class allows you to select all elements except those that satisfy a certain condition. This is extremely useful as it helps us avoid having to write extra css to override general styles.</p> <p>For example, I may want all links on my site to have an underline, except ones which I specify. Normally, I would write -</p> <pre><code>a { text-decoration: underline; } a.no-underline { text-decoration: none; } </code></pre> <p>Doing this means that the links with the class .no-underline have the default styling unnecessarily applied to them. Using the :not selector, I can avoid this extra declaration -</p> <pre><code>a:not(.no-underline) { text-decoration: underline; } a.no-underline { text-decoration: none; } </code></pre> <p><a href="https://twitter.com/wesbos">Wesbos on twitter</a> also recently showed an example of how we can use this selector -</p> <blockquote> <p>🔥Protip: Use CSS :not() instead of applying and unapplying borders on navigations. Supported wherever last-child is <a href="https://t.co/HPJ1Rw3jZH">pic.twitter.com/HPJ1Rw3jZH</a> — Wes Bos (@wesbos) <a href="https://twitter.com/wesbos/status/606144483562913792">June 3, 2015</a></p> </blockquote> <h2 id="lang" tabindex="-1">:lang <a class="header-anchor" href="https://bitsofco.de/5-lesser-used-css-selectors/">#</a></h2> <p>The lang pseudo-class targets elements that have been defined as a specific language. Selecting these elements, we can do a number of things.</p> <p><strong>1. Add helpful information and/or styling</strong>. We can style the section of the page that is in a different language in a special way to make it more clear to users, as well as add helpful information about the section.</p> <pre><code>*:lang(de) { border-left: 4px solid #FFCE00; padding-left: 1rem; } *:lang(de):before { content: &quot;DE&quot;; position: absolute; top: 0; left: -55px; width: 40px; height: 40px; text-align: center; padding: 10px 0; border-radius: 50%; background-color: #FFCE00; } </code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/Ww1p65NeLU-700.avif 700w"><source type="image/webp" srcset="https://bitsofco.de/img/Ww1p65NeLU-700.webp 700w"><img alt="Styling a paragraph in German" loading="lazy" decoding="async" src="https://bitsofco.de/img/Ww1p65NeLU-700.png" width="700" height="223"></picture></p> <p><strong>2. Use special quotation marks</strong>. Different languages use different quotation marks, so it is helpful to be able to use the native quotation marks of the relevant language.</p> <pre><code> blockquote:before { content: open-quote; padding-right: 1rem; } blockquote:after { content: close-quote; padding-left: 1rem; } blockquote:lang(fr) { quotes: '«' '»'; } blockquote:lang(zh-Hans) { quotes: '「' '」'; } </code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/1H-UA0swFX-700.avif 700w"><source type="image/webp" srcset="https://bitsofco.de/img/1H-UA0swFX-700.webp 700w"><img alt="Styling Quotation Marks for French and Chinese Languages" loading="lazy" decoding="async" src="https://bitsofco.de/img/1H-UA0swFX-700.png" width="700" height="303"></picture></p> <p><strong>3. Apply special fonts</strong>. We can also apply fonts or font styles more suited to the language if needed. For example, in Japanese, using italicised letters for emphasis is not suitable. Instead, we can use use the dot style that is common to the language.</p> <pre><code>em:not(lang(ja)) { font-style: italic; } em:lang(ja) { font-style: normal; -webkit-text-emphasis-style: dot; text-emphasis-style: dot; text‑emphasis‑position: over right; } </code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/zjLmQwb-x_-700.avif 700w"><source type="image/webp" srcset="https://bitsofco.de/img/zjLmQwb-x_-700.webp 700w"><img alt="Styling emphasis in Japanese language using dots" loading="lazy" decoding="async" src="https://bitsofco.de/img/zjLmQwb-x_-700.png" width="700" height="101"></picture></p> <h2 id="target" tabindex="-1">:target <a class="header-anchor" href="https://bitsofco.de/5-lesser-used-css-selectors/">#</a></h2> <p>The :target psuedo-class represents an element that is the target of a referring URI. For example, if you have a particularly long article broken up into sections, with each section having a unique ID, you can link to that particular section in the URL.</p> <p>Using the :target selector, you can style whichever section is the current target.</p> <pre><code>:target { border-left: 4px solid rgb(200, 200, 200); padding-left: 1rem; } </code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/E34WhKMtVI-700.avif 700w"><source type="image/gif" srcset="https://bitsofco.de/img/E34WhKMtVI-700.gif 700w"><img alt="Highlighting the current target" loading="lazy" decoding="async" src="https://bitsofco.de/img/E34WhKMtVI-700.webp" width="700" height="402"></picture></p> <p>As always, you can view the full code examples on my <a href="https://github.com/ireade/css-selectors">github repo</a>.</p> The Accessibility Cheatsheet 2015-06-06T00:00:00Z https://bitsofco.de/the-accessibility-cheatsheet/ <p>We all know that accessibility is important. The problem is, it is not always clear what exactly we can do to make our sites more accessible.</p> <p>The <a href="https://www.w3.org/WAI/">Web Accessibility Initiative</a> created some <a href="https://www.w3.org/WAI/intro/wcag.php">Web Content Accessibility Guidelines</a> (WCAG) targeted at us, web content developers, to create more accessible websites. The WCAG contain some very useful information, and so I decided to condense the very extensive guidelines and highlight some practical examples of what we can do to implement them and make our websites more accessible.</p> <h2 id="overview" tabindex="-1">Overview <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h2> <p>The guidelines for accessible content have four overarching principles, each with more specific guidelines. You can click on the link to go to the relevant section of this article.</p> <ul> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">1</a> - &quot;Perceivable&quot; - Information and user interface components must be presentable to users in ways they can perceive. <ul> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">1.1</a> - Text Alternatives</li> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">1.2</a> - Alternatives for Time-Based Media</li> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">1.3</a> - Adaptable Content</li> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">1.4</a> - Distinguishable</li> </ul> </li> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">2</a> - &quot;Operable&quot; - User interface components and navigation must be operable. <ul> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">2.1</a> - Keyboard Accessible</li> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">2.2</a> - Enough Time</li> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">2.3</a> - Seizures</li> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">2.4</a> - Navigable</li> </ul> </li> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">3</a> - &quot;Understandable&quot; - Information and the operation of user interface must be understandable. <ul> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">3.1</a> - Readable</li> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">3.2</a> - Predictable</li> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">3.3</a> - Input Assistance</li> </ul> </li> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">4</a> - &quot;Robust&quot; - Content must be robust enough that it can be interpreted reliably by a wide variety of user agents, including assistive technologies. <ul> <li><a href="https://bitsofco.de/the-accessibility-cheatsheet/">4.1</a> - Compatible</li> </ul> </li> </ul> <h2 id="principle-1-perceivable" tabindex="-1">Principle 1 - &quot;Perceivable&quot; <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h2> <h3 id="1-1-text-alternatives" tabindex="-1">1.1 Text alternatives <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h3> <blockquote> <p>&quot;All non-text content that is presented to the user has a text alternative that serves the equivalent purpose&quot;</p> </blockquote> <p>Plain text is the optimal format for any piece of content. This is because it can be used in many different formats to suit individuals with different disabilities. Therefore, it is important to provide a plain text alternative format for all content that is informative, i.e. not just decorative.</p> <p><strong>For images, use the <code>alt</code> attribute</strong>. The alternative text for an image should be as descriptive as possible, such that the same message is conveyed.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>newsletter.gif<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>Free newsletter. Get free recipes, news, and more.<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre> <p><strong>For audio and video elements, provide text transcripts</strong>. You can use the <a href="https://www.w3.org/html/wg/drafts/html/CR/embedded-content-0.html#the-track-element"><code>track</code> element</a> to specify timed text tracks for these media elements.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token comment">&lt;!-- Format of the track element --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>track</span> <span class="token attr-name">kind</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>subtitles | captions | descriptions<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>path/to/file.vtt<span class="token punctuation">"</span></span> <span class="token attr-name">srclang</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 attr-name">label</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><br><span class="token comment">&lt;!-- Example caption for an audio file --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>audio</span> <span class="token attr-name">controls</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>source</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>myaudio.ogg<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>audio/ogg<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">&lt;</span>track</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>caption_en.vtt<span class="token punctuation">"</span></span> <span class="token attr-name">kind</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>captions<span class="token punctuation">"</span></span> <span class="token attr-name">srclang</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">label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>English<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">&lt;/</span>audio</span><span class="token punctuation">></span></span><br><br><span class="token comment">&lt;!-- Example descriptions of a video file in English and German --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>video</span> <span class="token attr-name">poster</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>myvideo.png<span class="token punctuation">"</span></span> <span class="token attr-name">controls</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>source</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>myvideo.mp4<span class="token punctuation">"</span></span> <span class="token attr-name">srclang</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">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>video/mp4<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">&lt;</span>track</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>description_en.vtt<span class="token punctuation">"</span></span> <span class="token attr-name">kind</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>descriptions<span class="token punctuation">"</span></span> <span class="token attr-name">srclang</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">label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>English<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">&lt;</span>track</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>description_de.vtt<span class="token punctuation">"</span></span> <span class="token attr-name">kind</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>descriptions<span class="token punctuation">"</span></span> <span class="token attr-name">srclang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>de<span class="token punctuation">"</span></span> <span class="token attr-name">label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>German<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">&lt;/</span>video</span><span class="token punctuation">></span></span></code></pre> <p><strong>For user interface elements, use labels</strong>. Labels can be used to provide context for information that may be otherwise very clear visualy. For example, where you may have a primary and secondary navigation that is styled differently, you use <code>aria-label</code> to distinguish between them.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</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>navigation<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Primary<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">&lt;</span>ul</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span>...a list of links here ...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</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>navigation<span class="token punctuation">"</span></span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Secondary<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">&lt;</span>ul</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">></span></span>...a list of links here ...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <h3 id="1-2-alternatives-for-time-based-media" tabindex="-1">1.2 Alternatives for time-based media <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h3> <blockquote> <p>&quot;Provide alternatives for time-based media.&quot;</p> </blockquote> <p>Time-based media (audio and video) can be especially difficult for individuals with hearing or vision difficulties. In addition to providing a plain text alternative, it may also be helpful to <strong>provide an alternative time-based media version</strong>. For example -</p> <ul> <li>Sign language as part of a video file</li> <li>Alternative audio for video files</li> <li>Video file with sign language as alternative for audio files</li> </ul> <h3 id="1-3-adaptable-content" tabindex="-1">1.3 Adaptable Content <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h3> <blockquote> <p>&quot;Create content that can be presented in different ways (for example simpler layout) without losing information or structure.&quot;</p> </blockquote> <p><strong>Write your HTML in a meaningful sequence</strong>. Your document should be readable and understandable without any CSS. Lay out your HTML the way the page is inteaded to be read and, where possible, make use of semantic markup.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>header</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Site Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>nav</span><span class="token punctuation">></span></span><span class="token comment">&lt;!-- links --></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>nav</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>header</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Page Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>section</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h2</span><span class="token punctuation">></span></span>Section Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Lorem ipsum dolor sit amet, <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">></span></span>consectetur<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">></span></span> adipiscing elit. Pauca mutat vel plura sane;<br> Vide, quantum, inquam, fallare, Torquate. Iam in altera philosophiae parte.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>section</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>footer</span><span class="token punctuation">></span></span><br> <span class="token comment">&lt;!-- Site credit --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>footer</span><span class="token punctuation">></span></span></code></pre> <p><strong>Meaningful information should not be conveyed solely via sensory characteristics</strong>. Sensory characteristics such as shape, size, visual location, orientation, or sound should not be the only way of conveying important information.</p> <p>If you want to convey that a button will delete content, for example, make sure that this is also written in text, as shown on the left. Do not rely solely on colour and icons, as shown on right.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/CjgV3Sna4c-320.avif 320w"><source type="image/webp" srcset="https://bitsofco.de/img/CjgV3Sna4c-320.webp 320w"><img alt="Red Button With Delete Text vs Red Button With Trash Symbol" loading="lazy" decoding="async" src="https://bitsofco.de/img/CjgV3Sna4c-320.png" width="320" height="103"></picture></p> <h3 id="1-4-distinguishable" tabindex="-1">1.4 Distinguishable <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h3> <blockquote> <p>&quot;Make it easier for users to see and hear content including separating foreground from background.&quot;</p> </blockquote> <p><strong>Contrast ratio of text to background should be at least 4.5:1, preferably 7:1</strong>. You can use <a href="https://leaverou.github.io/contrast-ratio/">Lea Verou's app to find the contrast ratio</a> of your site's colours.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/HMFiqH2elN-700.avif 700w"><source type="image/webp" srcset="https://bitsofco.de/img/HMFiqH2elN-700.webp 700w"><img alt="Example of Contrast Ratio of 5:1 with white bg and text of colour rgb 110 110 110" loading="lazy" decoding="async" src="https://bitsofco.de/img/HMFiqH2elN-700.png" width="700" height="442"></picture></p> <p><strong>Text should be easily resizable</strong>. Text should be resizable using the default browser mechanisms up to 200% without a loss of content or functionality.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/iJtBKjVFhT-700.avif 700w"><source type="image/gif" srcset="https://bitsofco.de/img/iJtBKjVFhT-700.gif 700w"><img alt="Text Readable at 100% and 200% zoom" loading="lazy" decoding="async" src="https://bitsofco.de/img/iJtBKjVFhT-700.webp" width="700" height="461"></picture></p> <p><strong>Use actual text instead of images of text</strong>. As mentioned before, plain text is the most accessible format to use. Therefore, it is counterintuitive to use images of text where plain text can be used.</p> <p><strong>Control over audio media should be provided</strong>. If any audio is played on a web page, provide a mechanism for users to control it with pause/play buttons and volume controls independent of the system volume controls.</p> <h2 id="principle-2-operable" tabindex="-1">Principle 2 - &quot;Operable&quot; <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h2> <h3 id="2-1-keyboard-accessible" tabindex="-1">2.1 Keyboard accessible <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h3> <blockquote> <p>&quot;Make all functionality available from a keyboard.&quot;</p> </blockquote> <p>Many people are unable to navigate the web using a mouse. Therefore, all functionality should be operable through the standard keyboard interface without requiring specific timings for individual keys.</p> <p><strong>Ensure all functional elements have a clear focus state</strong>. For people navigating a website using the tab key only, focus states are how they know their location on the page. You can use javascript to <a href="https://www.w3.org/TR/2014/NOTE-WCAG20-TECHS-20140916/SCR29">add keyboard accessibility to static elements</a> if needed.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/ZCDHwnLTqH-700.avif 700w"><source type="image/gif" srcset="https://bitsofco.de/img/ZCDHwnLTqH-700.gif 700w"><img alt="Showing Focus States of Elements on Bitsofcode Website" loading="lazy" decoding="async" src="https://bitsofco.de/img/ZCDHwnLTqH-700.webp" width="700" height="469"></picture></p> <p><strong>Avoid keyboard traps</strong>. Tab through the content of your website from start to finish to ensure that the keyboard focus is not trapped on any of the content.</p> <h3 id="2-2-enough-time" tabindex="-1">2.2 Enough time <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h3> <blockquote> <p>&quot;Provide users enough time to read and use content.&quot;</p> </blockquote> <p><strong>Provide controls for timed content</strong>. For any interactions related to timing - including moving information, auto-updating, or page time-outs - you should implement at least one of the following safeguards -</p> <ul> <li>Users can turn off the time limiit</li> <li>Users can adjust time limit to at least 10 times the length of the default setting</li> <li>Users is warned before time expires and given at least 20 seconds to extend the time limit with a simple action</li> </ul> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/dJWIXrmUZH-397.avif 397w"><source type="image/gif" srcset="https://bitsofco.de/img/dJWIXrmUZH-397.gif 397w"><img alt="HSBC Online Banking timeout message" loading="lazy" decoding="async" src="https://bitsofco.de/img/dJWIXrmUZH-397.webp" width="397" height="249"></picture></p> <h3 id="2-3-seizures" tabindex="-1">2.3 Seizures <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h3> <blockquote> <p>&quot;Do not design content in a way that is known to cause seizures.&quot;</p> </blockquote> <p><strong>Flashing light should not occur more than three times per second</strong>. Or, the flash should be below the <a href="https://www.w3.org/TR/2008/REC-WCAG20-20081211/#general-thresholddef">general flash and red flash thresholds</a>. You can use <a href="https://trace.wisc.edu/peat/">photosensitive epilepsy analysis tools</a> or <a href="https://www.onlineflashtest.com/">flash tests</a> to test your site if you are unsure.</p> <h3 id="2-4-navigable" tabindex="-1">2.4 Navigable <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h3> <blockquote> <p>&quot;Provide ways to help users navigate, find content, and determine where they are.&quot;</p> </blockquote> <p><strong>Provide a link for users to skip to the page's main content</strong>. One of the first links on every page of a website should include a link for users to bypass repeated blocks of content, such as the navigation. This is especially important for pages that have large, multi-layered navigation menus. The link itself does not need to be visible when out of focus. For example -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>style</span><span class="token punctuation">></span></span><span class="token style"><span class="token language-css"><br> <span class="token selector">#skip_to</span> <span class="token punctuation">{</span><br> <span class="token property">position</span><span class="token punctuation">:</span> fixed<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">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token property">opacity</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token selector">#skip_to:focus</span> <span class="token punctuation">{</span><br> <span class="token property">opacity</span><span class="token punctuation">:</span> 1<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">&lt;/</span>style</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>#main_content<span class="token punctuation">"</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>skip_to<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Skip to Main Content<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>nav</span><span class="token punctuation">></span></span> <span class="token comment">&lt;!-- Navigations links here --></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>nav</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>main_content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> <span class="token comment">&lt;!-- Main content here --></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span></code></pre> <p><strong>Titles should be meaningful</strong>. The title of the web page, as well as the page heading, section headings, and labels, should describe the topic or purpose of the page.</p> <p><strong>Link purpose can be determined from link text</strong>. As far as is possible, the purpose of a link should be able to be determined from the text that is within the anchor tag itself.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/D_uJXAFDDw-431.avif 431w"><source type="image/webp" srcset="https://bitsofco.de/img/D_uJXAFDDw-431.webp 431w"><img alt="Proper placement of anchor tag around meaningful text. Anchor tag around the words click here vs around the words more posts about HTML" loading="lazy" decoding="async" src="https://bitsofco.de/img/D_uJXAFDDw-431.png" width="431" height="122"></picture></p> <p><strong>Provide more than one way to locate a web page</strong>. The same page should be accessible by more than just one link on one page. For example, a site could have -</p> <ul> <li>Complete site map on a single page</li> <li>Search function to access all content</li> <li>Navigation with links to all pages</li> </ul> <p><strong>Provide information about the current location</strong>. It is useful to provide information about where the current page is in relation to the rest of the website. This can be achieved with any of the following -</p> <ul> <li>Breadcrumbs</li> <li>Site map</li> <li>Highlighting the current location in navigation</li> <li>Using the <code>&lt;link rel=&quot;index | next | prev | contents&quot;&gt;</code> tag to specify the current page's relationship to other pages</li> </ul> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/GCcbGN9Nh--700.avif 700w"><source type="image/gif" srcset="https://bitsofco.de/img/GCcbGN9Nh--700.gif 700w"><img alt="Highlighting the current location in navigation on Designer News" loading="lazy" decoding="async" src="https://bitsofco.de/img/GCcbGN9Nh--700.webp" width="700" height="427"></picture></p> <h2 id="principle-3-understandable" tabindex="-1">Principle 3 - 'Understandable&quot; <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h2> <h3 id="3-1-readable" tabindex="-1">3.1 Readable <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h3> <blockquote> <p>&quot;Make text content readable and understandable.&quot;</p> </blockquote> <p><strong>Specify the language(s) of the page</strong>. Specify the language of the current page on the HTML element, and any languages of specific parts.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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> <span class="token comment">&lt;!-- Language of the page is English --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Page Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Health goth American Apparel quinoa, jean shorts cray you probably haven't heard of them Schlitz<br>occupy actually tofu distillery disrupt letterpress fixie. Slow-carb keytar hella, actually B<br>ushwick irony semiotics Portland readymade photo booth taxidermy pork belly small batch try-hard yr.<br>Thundercats blog normcore, tousled American Apparel art party.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br><span class="token comment">&lt;!-- Language of this blockquote is German --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>de<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> Da dachte der Herr daran, ihn aus dem Futter zu schaffen,<br> aber der Esel merkte, daß kein guter Wind wehte, lief fort<br> und machte sich auf den Weg nach Bremen: dort, meinte er,<br> könnte er ja Stadtmusikant werden.<br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>blockquote</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>Health goth American Apparel quinoa, jean shorts cray you probably haven't heard of them Schlitz<br>occupy actually tofu distillery disrupt letterpress fixie. Slow-carb keytar hella, actually B<br>ushwick irony semiotics Portland readymade photo booth taxidermy pork belly small batch try-hard yr.<br>Thundercats blog normcore, tousled American Apparel art party.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre> <p><strong>Provide meanings of unusual words and pronunciations of difficult words</strong>. You can use the <code>title</code> attribute to provide the meaning of abbreviations and unusual words. For definitions, you can use the <code>dl</code> element to provide a definition list.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token comment">&lt;!-- Providing meaning inline --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>Austin Rocks<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Au5t1N r0xx0rz<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>abbr</span><span class="token punctuation">></span></span><br><br><span class="token comment">&lt;!-- Using a definition list --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>That was a <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>#d-humblebrag<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>humble brag<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dl</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dt</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>d-humblebrag<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Humble Brag<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dt</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>dd</span><span class="token punctuation">></span></span>Subtly letting others now about how fantastic your life is while undercutting<br> it with a bit of self-effacing humor or "woe is me" gloss.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dd</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>dl</span><span class="token punctuation">></span></span></code></pre> <p><strong>Make content available at a lower secondary education reading level</strong>. Teenagers aged between 11-14 should be able to understand the content, even if specific terminology and concepts are new.</p> <h3 id="3-2-predictable" tabindex="-1">3.2 Predictable <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h3> <blockquote> <p>&quot;Make Web pages appear and operate in predictable ways.&quot;</p> </blockquote> <p><strong>Consistent navigation</strong>. Navigation elements should be repeated in a consistent way throughout the website.</p> <p><strong>Consistent identification</strong>. Terminology and repeatable elements should appear consistently throughout the website.</p> <p><strong>No unprovoked changes of context</strong>. Any changes of context should only happen on request by the user. Things like redirects, popups and other similar interactions should be communicated clearly beforehand.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>html</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">></span></span>The Tudors<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>refresh<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0;URL='https://thetudors.example.com/'<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">&lt;/</span>head</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">></span></span>This page has moved to a <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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://thetudors.example.com/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>theTudors.example.com<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">></span></span>.<br> You will now be redirected to the new site.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></code></pre> <h3 id="3-3-input-assistance" tabindex="-1">3.3 Input Assistance <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h3> <blockquote> <p>&quot;Help users avoid and correct mistakes&quot;</p> </blockquote> <p><strong>Provide labels and instructions</strong> - Provide labels or instructions for input elements. Where there is a commonly made error, provide suggestions that users can model their answers against.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/KjbZnC7VcB-676.avif 676w"><source type="image/webp" srcset="https://bitsofco.de/img/KjbZnC7VcB-676.webp 676w"><img alt="Exmaple showing cues to help with picking a password" loading="lazy" decoding="async" src="https://bitsofco.de/img/KjbZnC7VcB-676.png" width="676" height="294"></picture></p> <p><strong>Error messages in simple language</strong>. Errors made should be described to the user in plain, understandable text, not error codes.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/YrzKVdKbqZ-456.avif 456w"><source type="image/webp" srcset="https://bitsofco.de/img/YrzKVdKbqZ-456.webp 456w"><img alt="Error messages in plain text when picking a new password" loading="lazy" decoding="async" src="https://bitsofco.de/img/YrzKVdKbqZ-456.png" width="456" height="273"></picture></p> <p><strong>Error prevention</strong>. Where a user is submitting information, at least one of the following must be true -</p> <ul> <li>The submission of information is reversible</li> <li>The answers is checked for errors and the user is given the opportunity to correct before submission.</li> <li>The user is given the opportunity to confirm the information before submission</li> </ul> <h2 id="principle-4-robust" tabindex="-1">Principle 4 - &quot;Robust&quot; <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h2> <h3 id="4-1-compatible" tabindex="-1">4.1 Compatible <a class="header-anchor" href="https://bitsofco.de/the-accessibility-cheatsheet/">#</a></h3> <blockquote> <p>&quot;Maximize compatibility with current and future user agents, including assistive technologies.&quot;</p> </blockquote> <p><strong>Write valid code</strong>. Ensure the compatibility of your HTML by making sure it passes <a href="https://validator.w3.org/">validations checks</a>. Some important things validation checks look for include -</p> <ul> <li>Valid doctype (<a href="https://bitsofco.de/doctype">Learn more about doctype</a>)</li> <li>Valid character encoding specified (<a href="https://bitsofco.de/meta-charset">Learn more about character encoding</a>)</li> <li>Complete start and end tags</li> <li>No duplicate attributes, for examples IDs</li> </ul> <p><strong>Specify the purpose of elements</strong>. Specify the name, role and value for user interface components where appropriate. For forms in particular, <code>label</code>s should be used where possible -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</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>signupform<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">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>nm<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</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>nm<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>text<span class="token punctuation">"</span></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>name<span class="token punctuation">"</span></span> <span class="token attr-name">value</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><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>fieldset</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>legend</span><span class="token punctuation">></span></span>Would you like to sign up?<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>legend</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</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>yes<span class="token punctuation">"</span></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>request<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>yes<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>radio<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>yes<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Yes<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</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>no<span class="token punctuation">"</span></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>request<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no<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>radio<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>No<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>fieldset</span><span class="token punctuation">></span></span><br><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>submit<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">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</span><span class="token punctuation">></span></span></code></pre> <p>Where the <code>label</code> cannot be used, you can use the <code>title</code> attribute instead -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</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>searchform<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">&lt;</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">title</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>site search<span class="token punctuation">"</span></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>query<span class="token punctuation">"</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>q<span class="token punctuation">"</span></span> <span class="token attr-name">value</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">&lt;</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>submit<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>search<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">&lt;/</span>form</span><span class="token punctuation">></span></span></code></pre> <p><code>aria-label</code> can also be used to provide a label for a user interface element, where a label may not be present.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>box<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br> This is a pop-up box.<br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Close<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">'box'</span><span class="token punctuation">)</span><span class="token punctuation">.</span>style<span class="token punctuation">.</span>display<span class="token operator">=</span><span class="token string">'none'</span><span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><br> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>close-button<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> X <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span></code></pre> <p>If you would like to read more about this, you can read the <a href="https://www.w3.org/WAI/WCAG20/quickref/">Web Content Accessibility Guidelines Reference</a>, which goes into a lot more detail about how you can meet all the requirements.</p> <p>I think the best thing that we can do is try to navigate the websites we create using only the mechanisms that people with disabilities use, such as <a href="https://usabilitygeek.com/10-free-screen-reader-blind-visually-impaired-users/">screen readers</a>. Doing this has really made me aware of things I should change on the sites I have made to make them easier to use.</p> Viewport vs Percentage Units 2015-05-26T00:00:00Z https://bitsofco.de/viewport-vs-percentage-units/ <p>In my <a href="https://bitsofco.de/css-font-sizing">article about CSS font sizes</a>, I wrote about the (relatively) new viewport units. These units - vw, vh, vmin, and vmax - are based on the size of the browser viewport. Because their actual size changes depending on the viewport size, this makes them great units for responsive design. Although in my previous post I argued against using these units for font sizes, they can be very useful for defining layout elements.</p> <h2 id="the-viewport-units" tabindex="-1">The viewport units <a class="header-anchor" href="https://bitsofco.de/viewport-vs-percentage-units/">#</a></h2> <p>The viewport units are relative units, which means that they do not have an objective measurement. Instead, their size is determined by the size of the viewport. There are four units related to the viewport -</p> <table> <thead> <tr> <th>Name</th> <th>Unit</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>Viewport Width</td> <td>vw</td> <td>1/100th of the width of the viewport</td> </tr> <tr> <td>Viewport Height</td> <td>vh</td> <td>1/100th of the height of the viewport</td> </tr> <tr> <td>Minimum Viewport</td> <td>vmin</td> <td>1/100th of the viewport’s smaller dimension (height or width)</td> </tr> <tr> <td>Maximum Viewport</td> <td>vmax</td> <td>1/100th of the viewport’s larger dimension (height or width)</td> </tr> </tbody> </table> <p>I will focus on the first two units, as they are more likely to be used.</p> <p>In a lot of cases, the viewport units (vh and vw) and percentage unit overlap in terms of what they can achieve. However, they each have their clear strengths and weaknesses. To sum up -</p> <blockquote> <p>When dealing with widths, the % unit is more suitable. With heights, the vh unit is better.</p> </blockquote> <h2 id="full-width-elements-vw" tabindex="-1">Full Width Elements - % &gt; vw <a class="header-anchor" href="https://bitsofco.de/viewport-vs-percentage-units/">#</a></h2> <p>As I mentioned, the vw unit determines its size based on the width of the viewport. However, browsers calculate the viewport size as the browser window, which includes the space for the scrollbar.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/fOlfhKmzGo-1172.avif 1172w"><source type="image/webp" srcset="https://bitsofco.de/img/fOlfhKmzGo-1172.webp 1172w"><img alt="Viewport is Greater than HTML" loading="lazy" decoding="async" src="https://bitsofco.de/img/fOlfhKmzGo-1172.jpeg" width="1172" height="910"></picture></p> <p>If the page extends more than the height of the viewport - which will make the scrollbar appear - the width of the viewport will actually be larger than the width of the html element.</p> <blockquote> <p>Viewport &gt; html &gt; body</p> </blockquote> <p>Therefore, if you set an element as 100vw, the element will extend outside the html and body elements. In this example, I put the red border around the html element, and the background colour on the sections.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/dd6JeEIEz--700.avif 700w"><source type="image/webp" srcset="https://bitsofco.de/img/dd6JeEIEz--700.webp 700w"><img alt="Viewport is Greater than HTML 2" loading="lazy" decoding="async" src="https://bitsofco.de/img/dd6JeEIEz--700.png" width="700" height="443"></picture></p> <p>Because of this nuance, when making an element span the entire width of the page, it is best to use the percentage unit, rather than the viewport width.</p> <h2 id="full-height-elements-vh" tabindex="-1">Full Height Elements - vh &gt; % <a class="header-anchor" href="https://bitsofco.de/viewport-vs-percentage-units/">#</a></h2> <p>When making an element span the full height of a page, on the other hand, the vh unit is much better than the percentage unit.</p> <p>Because the size of an element defined in percentage is determined by its parent element, we can only have an element fill the entire height of the screen if the parent element also fills the entire height of the screen. This usually means that we have to position the element as fixed, in order to make the html element the parent element, or otherwise resort to some hack.</p> <p>With vh, however, it is as simple as writing:</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.example</span> <span class="token punctuation">{</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 100vh<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>No matter how nested the <code>.example</code> element is, it is still able to be sized relative to the viewport dimensions. The problem of the scrollbar is also not an issue because most pages don't typically have the horizontal scroll bar present.</p> <p>Here are some examples of how we can use the vh unit to easily create some designs.</p> <h3 id="the-fullscreen-background-image" tabindex="-1">The Fullscreen Background Image <a class="header-anchor" href="https://bitsofco.de/viewport-vs-percentage-units/">#</a></h3> <p>A typical use for the vh unit is for creating a background image that spans the entire height and width of the screen, no matter the size of the device. This is easily created with vh -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.bg</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> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">'bg.jpg'</span><span class="token punctuation">)</span></span> center/cover<span class="token punctuation">;</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 100vh<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/KpaH8HmLDp-400.avif 400w"><source type="image/gif" srcset="https://bitsofco.de/img/KpaH8HmLDp-400.gif 400w"><img alt="Fullscreen Background Image with vh" loading="lazy" decoding="async" src="https://bitsofco.de/img/KpaH8HmLDp-400.webp" width="400" height="256"></picture></p> <h3 id="fullscreen-sections-as-pages" tabindex="-1">Fullscreen Sections as &quot;Pages&quot; <a class="header-anchor" href="https://bitsofco.de/viewport-vs-percentage-units/">#</a></h3> <p>Similarly, we can also create the effect of having &quot;pages&quot; by making each section of the page span the full height and width of the viewport.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">section</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br> <span class="token property">height</span><span class="token punctuation">:</span> 100vh<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/5W_X4idklc-400.avif 400w"><source type="image/gif" srcset="https://bitsofco.de/img/5W_X4idklc-400.gif 400w"><img alt="Fullscreen Pages" loading="lazy" decoding="async" src="https://bitsofco.de/img/5W_X4idklc-400.webp" width="400" height="252"></picture></p> <p>We can use javascript to create the illusion of flipping through the pages.</p> <pre class="language-js" tabindex="0"><code class="language-js"><span class="token function">$</span><span class="token punctuation">(</span><span class="token string">'nav'</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">'click'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><br><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">hasClass</span><span class="token punctuation">(</span><span class="token string">'down'</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">var</span> movePos <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">scrollTop</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">height</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br> <span class="token punctuation">}</span><br> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token function">$</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">hasClass</span><span class="token punctuation">(</span><span class="token string">'up'</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span><br> <span class="token keyword">var</span> movePos <span class="token operator">=</span> <span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">scrollTop</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token function">$</span><span class="token punctuation">(</span>window<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">height</span><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 function">$</span><span class="token punctuation">(</span><span class="token string">'html, body'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">animate</span><span class="token punctuation">(</span><span class="token punctuation">{</span><br> <span class="token literal-property property">scrollTop</span><span class="token operator">:</span> movePos<br> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre> <h3 id="within-the-fold-images" tabindex="-1">Within the fold images <a class="header-anchor" href="https://bitsofco.de/viewport-vs-percentage-units/">#</a></h3> <p>The vh unit can also be used to control the size of images within the page. For example, within an article, we may want to make sure that any image can be viewed in its entirety on the page, no matter the size of the screen.</p> <p>To do that, we could set -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">img</span> <span class="token punctuation">{</span><br> <span class="token property">width</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span> <span class="token comment">/* Image width adjust to height to remain proportional */</span><br> <span class="token property">max-width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> <span class="token comment">/* Image doesn't exceed parent element's width */</span><br> <span class="token property">max-height</span><span class="token punctuation">:</span> 90vh<span class="token punctuation">;</span> <span class="token comment">/* Image doesn't exceed viewport height */</span><br><br> <span class="token property">margin</span><span class="token punctuation">:</span> 2rem auto<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/4FMo3jBU5q-400.avif 400w"><source type="image/gif" srcset="https://bitsofco.de/img/4FMo3jBU5q-400.gif 400w"><img alt="Within the Fold Images" loading="lazy" decoding="async" src="https://bitsofco.de/img/4FMo3jBU5q-400.webp" width="400" height="252"></picture></p> <h2 id="browser-support" tabindex="-1">Browser Support <a class="header-anchor" href="https://bitsofco.de/viewport-vs-percentage-units/">#</a></h2> <p>As these units are relatively new, there are still some issues with some browsers. Here is how to work around those problems -</p> <table> <thead> <tr> <th>Browser</th> <th>Issue</th> <th>Solution</th> </tr> </thead> <tbody> <tr> <td>iOS Safari 7.1</td> <td>“vh” can be buggy</td> <td>Target specific devices using media queries (<a href="https://gist.github.com/pburtchaell/e702f441ba9b3f76f587">see pburtchaell’s Gist</a>)</td> </tr> <tr> <td>Opera Mini 8, IE 8</td> <td>No support</td> <td>Use pburtchaell’s solution</td> </tr> <tr> <td>IE 9</td> <td>Accepts term “vm” instead of “vmin”</td> <td>Declare the size using both units, e.g.<code>.example {``width: 100vm;``width: 100vmin;``}</code></td> </tr> <tr> <td>IE 10-Edge</td> <td>No support for “vmax”</td> <td>Use pburtchaell’s solution</td> </tr> </tbody> </table> Building a Designer News Clone with AngularJS and Firebase 2015-05-19T00:00:00Z https://bitsofco.de/building-a-designer-news-clone/ <p>I recently discovered Firebase, and it's changed everything.</p> <p>If you haven't heard of it, <a href="https://www.firebase.com/">Firebase</a> is a platform that allows you to store and sync data in realtime. It essentially gives you the functionality of a backend database, but presented in a much simpler format, as it is NoSQL, and only using front-end code.</p> <p>You can create some surprisingly complex applications with it, so I decided to try and make something myself. I am relatively a beginner with both AngularJS and Firebase, so I settled on making something simple, a clone of <a href="https://news.layervault.com">Designer News</a>.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/7_6ForQa1z-990.avif 990w"><source type="image/webp" srcset="https://bitsofco.de/img/7_6ForQa1z-990.webp 990w"><img alt="Angular DN Homepage" loading="lazy" decoding="async" src="https://bitsofco.de/img/7_6ForQa1z-990.png" width="990" height="774"></picture></p> <p>You can actually see and <a href="https://ireade.github.io/ng-designer-news/#/">use the application here</a>, and <a href="https://github.com/ireade/ng-designer-news">view the code on github</a>.</p> <h2 id="project-overview" tabindex="-1">Project Overview <a class="header-anchor" href="https://bitsofco.de/building-a-designer-news-clone/">#</a></h2> <p>Although I can't go through the entire application, I will share how I organised the data, and how some specific actions were handled.</p> <h3 id="the-data-structure" tabindex="-1">The Data Structure <a class="header-anchor" href="https://bitsofco.de/building-a-designer-news-clone/">#</a></h3> <p>The structure of the data is incredibly important, and I spent a while trying to figure out the best way to do it so that I would have access to all the relevant data from certain controllers. In the end, I grouped the data into two main arrays - stories and users.</p> <p>Each user in the user array has the following data -</p> <pre><code>- first_name - last_name - title - email - id (uid from Firebase login via email) - karma - posts - post - title - date - id (Firebase key for post under &quot;stories&quot; array) - comments - comment - title (post title) - id (Firebase key for post under &quot;stories&quot; array) - date - comment (the text) </code></pre> <p>Each story in the stories array has the following data -</p> <pre><code>- title - date - url (null if description post) - description (null if url post) - category - voteCount - commentCount - voters (an array with uid of each person who has voted on the post) - comments - comment - date - text - voteCount - user - first_name - last_name - title - id (Firebase key for the user) - uid (uid from Firebase login via email) - voters (an array with uid of each person who has voted on the comment) </code></pre> <h3 id="creating-a-user" tabindex="-1">Creating a user <a class="header-anchor" href="https://bitsofco.de/building-a-designer-news-clone/">#</a></h3> <p>Creating a user is relatively simple with Firebase. There is a built in <a href="https://www.firebase.com/docs/web/guide/login/password.html">email and password authentication service</a>, which handles everything for you. Creating a new user is as simple as writing -</p> <pre><code>var ref = new Firebase(FIREBASE_URL); // Passing a &quot;user&quot; object ref.createUser({ email: user.email, password: user.password }) </code></pre> <p>When the user is created, they are given a token, the &quot;uid&quot;. Along with the other information given by the user, I pass in the &quot;uid&quot; to a new user in the users array.</p> <pre><code>var ref = new Firebase(FIREBASE_URL + '/users'); var users = $firebaseArray(ref); // Passing a &quot;user&quot; object users.$add({ uid: uid, first_name: user.first_name, last_name: user.last_name, title: user.title, email: user.email, karma: 0 }); </code></pre> <p>This creates a connection between the user created by the Firebase authentication service, which is referenced by the uid, and the user in the users array, which is referenced by the array key.</p> <p>When a user is created, we can check for the current user by looping through the users array and finding the user with the same uid as the current authorised user -</p> <pre><code>auth.$onAuth(function(authUser) { if (authUser) { users.$loaded().then(function(){ angular.forEach(users, function(user) { if (user.uid == authUser.uid) { $rootScope.currentUser = user; } }); // end loop }); // end users.$loaded } // end if }); </code></pre> <h3 id="adding-a-new-story" tabindex="-1">Adding a new story <a class="header-anchor" href="https://bitsofco.de/building-a-designer-news-clone/">#</a></h3> <p>When adding a new story, I had to make sure that I added the details to the &quot;stories&quot; array, but also to the story author's posts in the &quot;users&quot; array.</p> <p>Adding a new url story looked roughly like this -</p> <pre><code>var ref = new Firebase(FIREBASE_URL + '/stories'); var stories = $firebaseArray(ref); var userRef = new Firebase(FIREBASE_URL + '/users/' + $rootScope.currentUser.$id + '/posts'); var thisUser = $firebaseArray(userRef); $scope.addStory = function(story) { // Checks for errors go here // If no errors stories.$add({ title: story.title, url: story.url, category: storyCategory, // custom function used to get category description: null, date: Firebase.ServerValue.TIMESTAMP, user: { first_name: $rootScope.currentUser.first_name, last_name: $rootScope.currentUser.last_name, title: $rootScope.currentUser.title, uid: $rootScope.currentUser.uid, id: $rootScope.currentUser.$id }, commentCount: 0, voteCount: 0 }).then(function(ref){ thisUser.$add({ title: story.title, date: Firebase.ServerValue.TIMESTAMP, id: ref.key() }) }); } </code></pre> <h3 id="voting" tabindex="-1">Voting <a class="header-anchor" href="https://bitsofco.de/building-a-designer-news-clone/">#</a></h3> <p>The voting system, although a relatively small part of the application, had a lot of moving parts. It worked like this -</p> <ul> <li>Check if user has voted by looping through the array of voters</li> <li>If has voted, stop here</li> <li>If not, allow voting <ul> <li>Add 1 to current voteCount</li> <li>Add the current user to the list of voters on the post</li> <li>Add 1 to the story author's karma</li> </ul> </li> </ul> <pre><code>$scope.upvote = function(story) { var thisStoryRef = new Firebase(FIREBASE_URL + '/stories/' + story.$id); var thisStory = $firebaseObject(thisStoryRef); var votersRef = new Firebase(FIREBASE_URL + '/stories/' + story.$id + '/voters'); var voters = $firebaseArray(votersRef); var hasVoted = false; // Check if user has voted by looping through the array of voters voters.$loaded().then(function() { angular.forEach(voters, function(object, id) { if (object.$value == $rootScope.currentUser.uid) { hasVoted = true; } }) }).then(function() { // If has voted, stop here if (hasVoted) { $scope.alertMessage = { message: 'You have already voted on this post!', type: 'warning' }; // If not, allow voting } else { // Add 1 to current voteCount thisStory.voteCount++; thisStory.$save(); // Add the current user to the list of voters on the post voters.$add($rootScope.currentUser.uid); var storyAuthorRef = new Firebase(FIREBASE_URL + '/users/' + story.user.id); var storyAuthor = $firebaseObject(storyAuthorRef); // Add 1 to the story author's karma storyAuthor.$loaded().then(function() { storyAuthor.karma++; storyAuthor.$save(); }) } }) // end .then from voters.loaded }; </code></pre> <h2 id="more-to-do" tabindex="-1">More to do <a class="header-anchor" href="https://bitsofco.de/building-a-designer-news-clone/">#</a></h2> <p>This was just an insight into how I handled some of the functions of the application. If you want to check out the full code, you can view it on my github. I was playing around with the idea of making a screencast where I go through how I did everything. If that's something you would be interested in, let me know.</p> <p>Although the core functionality it works, it is definitely not production ready. I still have a lot of messages being logged to the console. In the future I want to work more on handling the error messages more effectively, especially related to authentication.</p> <p>As always, I would like any feedback I can get on this, so leave a comment below!</p> <h3 id="part-2" tabindex="-1">Part 2 <a class="header-anchor" href="https://bitsofco.de/building-a-designer-news-clone/">#</a></h3> <p>Read part 2 of this series, <a href="https://bitsofco.de/designer-news-clone-part-2-implementing-firebase-security">'Implementing Firebase Security'</a></p> CSS Vendor Prefixes 2015-05-12T00:00:00Z https://bitsofco.de/css-vendor-prefixes/ <p>When dealing with CSS, we undoubtedly come across vendor prefixes. Even though they are so widely used, there is still some unclarity regarding when they should be used and even why they should be used at all. In this post, I answer -</p> <ul> <li>What exactly are CSS vendor prefixes?</li> <li>Why do we need to use them?</li> <li>How do we properly use them?</li> <li>Which properties do we need to use them for?</li> </ul> <h2 id="what-are-css-vendor-prefixes" tabindex="-1">What are CSS Vendor Prefixes? <a class="header-anchor" href="https://bitsofco.de/css-vendor-prefixes/">#</a></h2> <p>CSS vendor prefixes are are a string of characters relating to specific browser engines that we place before a CSS property name. They can have either of the following formats -</p> <pre><code>'-' + vendor identifier + '-' + meaningful name '_' + vendor identifier + '-' + meaningful name </code></pre> <p>Here are some popular browsers and their corresponding identifiers -</p> <table> <thead> <tr> <th>Prefix</th> <th>Browser(s)</th> </tr> </thead> <tbody> <tr> <td>-webkit-</td> <td>Google Chrome, Safari, Android Browser</td> </tr> <tr> <td>-moz-</td> <td>Firefox</td> </tr> <tr> <td>-o-</td> <td>Opera</td> </tr> <tr> <td>-ms-</td> <td>Internet Explorer, Edge</td> </tr> <tr> <td>-khtml-</td> <td>Konqueror</td> </tr> </tbody> </table> <p>So, for exmaple, a Firefox prefix for the transform property will be written as -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.example</span> <span class="token punctuation">{</span><br> <span class="token property">-moz-transform</span><span class="token punctuation">:</span> value<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Although they are typically used before the property name, they can also be used to prefix specifc parts of the property value, as is shown in the exmaple below.</p> <h2 id="why-do-we-use-them" tabindex="-1">Why do we use them? <a class="header-anchor" href="https://bitsofco.de/css-vendor-prefixes/">#</a></h2> <p>Before new properties are formally added to CSS, browsers have the ability to test them out using their own methods of implementation.</p> <p>For example, when gradient backgrounds were first introduced, different browsers required different syntax to implement the same effect. To create a simple black to white gradient effect, we had to write -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.example</span> <span class="token punctuation">{</span><br> <span class="token comment">/* Safari 4-5, Chrome 1-9 */</span><br> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">-webkit-gradient</span><span class="token punctuation">(</span>linear<span class="token punctuation">,</span> left top<span class="token punctuation">,</span> left bottom<span class="token punctuation">,</span> <span class="token function">color-stop</span><span class="token punctuation">(</span>0%<span class="token punctuation">,</span> #000<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">color-stop</span><span class="token punctuation">(</span>100%<span class="token punctuation">,</span> #fff<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">/* Safari 5.1, Chrome 10+ */</span><br> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">-webkit-linear-gradient</span><span class="token punctuation">(</span>top<span class="token punctuation">,</span> #000 0%<span class="token punctuation">,</span> #fff 100%<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">/* Firefox 3.6+ */</span><br> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">-moz-linear-gradient</span><span class="token punctuation">(</span>top<span class="token punctuation">,</span> #000 0%<span class="token punctuation">,</span> #fff 100%<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">/* IE 6 - 9 */</span><br> <span class="token property">filter</span><span class="token punctuation">:</span> <span class="token property">progid</span><span class="token punctuation">:</span>DXImageTransform.Microsoft.<span class="token function">gradient</span><span class="token punctuation">(</span> startColorstr=<span class="token string">'#000000'</span><span class="token punctuation">,</span> endColorstr=<span class="token string">'#ffffff'</span><span class="token punctuation">,</span>GradientType=0 <span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">/* IE 10+ */</span><br> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">-ms-linear-gradient</span><span class="token punctuation">(</span>top<span class="token punctuation">,</span> #000 0%<span class="token punctuation">,</span> #fff 100%<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br> <span class="token comment">/* Opera 11.10+ */</span><br> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">-o-linear-gradient</span><span class="token punctuation">(</span>top<span class="token punctuation">,</span> #000 0%<span class="token punctuation">,</span> #fff 100%<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>In order to allow this differing implementation between browsers during the testing phase, the vendor-prefixing policy was introduced in CSS2.1. Each browser engine has it's own prefix, which, combined with the property name, essentially acts as its own property. Each browser only recognises properties with its own prefix and ignores the others.</p> <p>This avoids any clashes if and when the property becomes official. At this stage, when we write the unprefixed property name alone, it should be implemented in the same way across browsers.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.example</span> <span class="token punctuation">{</span><br> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span>top<span class="token punctuation">,</span> #000 0%<span class="token punctuation">,</span> #fff 100%<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>Although it may be a bit of a hassle to deal with them, vendor prefixes allow us to test out new features earlier than we would be able to if we had to wait for standardisation.</p> <h2 id="how-do-we-use-them" tabindex="-1">How do we use them? <a class="header-anchor" href="https://bitsofco.de/css-vendor-prefixes/">#</a></h2> <p>The proper way to use vendor-prefixed properties is to place them before the unprefixed property.</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">.example</span> <span class="token punctuation">{</span><br> <span class="token property">-webkit-animation-name</span><span class="token punctuation">:</span> slidein<span class="token punctuation">;</span><br> <span class="token property">-o-animation-name</span><span class="token punctuation">:</span> slidein<span class="token punctuation">;</span><br> <span class="token property">-ms-animation-name</span><span class="token punctuation">:</span> slidein<span class="token punctuation">;</span><br> <span class="token property">-moz-animation-name</span><span class="token punctuation">:</span> slidein<span class="token punctuation">;</span><br> <span class="token property">animation-name</span><span class="token punctuation">:</span> slidein<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>We do this to take advantage of the cascading nature of CSS. Browsers will use the last declared version of a property they can understand. By putting the unprefixed version last, we ensure that, when browsers eventually support the official property, that is what they will use.</p> <h2 id="which-properties-need-a-prefix" tabindex="-1">Which properties need a prefix? <a class="header-anchor" href="https://bitsofco.de/css-vendor-prefixes/">#</a></h2> <p>The list of properties we need to use vendor prefixes for is always changing. One place to start is looking at the <a href="https://www.w3.org/TR/css-2010/#properties">official property index</a>. If a property is not on there, it may be because it is still in the testing phase and it might be a good idea to use the vendor prefixes.</p> <p><strong>However</strong>, this is not the case for all properties, and not all browsers use a prefixed version of a property. An example of this is the <code>border-radius</code> property. Even though it is not on the official property index, the unprefixed version is supported by all modern major browsers. Only a small percentage of Webkit and Mozilla browsers ever needed the prefixed versions, and Opera and IE never used a prefixed version.</p> <p>Therefore, for a more current idea of which properties need a prefix, <a href="https://caniuse.com">caniuse.com</a> is the best place. Using their data, I created a site, &quot;<a href="https://ireade.github.io/which-vendor-prefix/">Which Vendor Prefix?</a>&quot;, that displays all the properties that need (or ever needed) a vendor prefix. It tells you which versions of the browser needs a prefixed version of the property to work, and you can decide whether to use a prefix or not base that. For example -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/CCPR_c06Cb-600.avif 600w"><source type="image/webp" srcset="https://bitsofco.de/img/CCPR_c06Cb-600.webp 600w"><img alt="Border-Radius Vendor Prefix" loading="lazy" decoding="async" src="https://bitsofco.de/img/CCPR_c06Cb-600.png" width="600" height="478"></picture></p> <h2 id="keep-calm-and-autoprefix" tabindex="-1">Keep calm and autoprefix <a class="header-anchor" href="https://bitsofco.de/css-vendor-prefixes/">#</a></h2> <p>Keeping track of which properties need to be prefixed can be confusing and taxing on your time. Luckily, there are ways to get around having to manually write each vendor prefix.</p> <ul> <li>CSS Preprocessor Mixins, e.g. <a href="https://bourbon.io/">Bourbon</a>, <a href="https://compass-style.org/">Compass</a></li> <li><a href="https://prefixr.com/index.php">Prefixr.com</a>: paste in your css and it adds the prefixes for you</li> <li><a href="https://www.npmjs.com/package/gulp-autoprefixer">Autoprefixr for Gulp</a></li> <li><a href="https://github.com/sindresorhus/sublime-autoprefixer">Autoprefixer for Sublime Text</a></li> </ul> <p>I'm sure there are many more ways to get around writing everything by hand. I'd love to know what you do, so leave a comment below!</p> Working With Document Alternates 2015-05-05T00:00:00Z https://bitsofco.de/working-with-document-alternates/ <p>There are circumstances in which we may want to provide our website users alternate versions of the same content. These cases usually fall into one of the following categories -</p> <ul> <li>Providing themed versions of a website with alternate stylesheets</li> <li>Providing versions of the website in different languages</li> <li>Serving a syndication feed for use by rss readers</li> <li>Providing the content of the page in alternate media formats</li> </ul> <p>In most of these cases, the alternate value of link tag is used.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>alternate<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>The alternate value tells the user agent or web crawler that the linked page or document has essentially the same content as the main page, but with some peripheral difference.</p> <p>The alternate value should be distinguished from the <code>canonical</code> value, which specifies which page is the &quot;preferred&quot; version. This is relevant when you may have the exact same content on more than one page.</p> <h2 id="alternate-stylesheets" tabindex="-1">Alternate Stylesheets <a class="header-anchor" href="https://bitsofco.de/working-with-document-alternates/">#</a></h2> <p>One way the alternate link tag can be used is to provide alternate stylesheets for a page. This allows us to, for example, provide a light or dark alternative theme for the website.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>stylesheet<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>main.css<span class="token punctuation">"</span></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>Light Theme<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">&lt;</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>alternate stylesheet<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>dark.css<span class="token punctuation">"</span></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>Dark Theme<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>Pairing the alternate value with the stylesheet value is not like just adding another stylesheet. The stylesheet marked as alternate is not actually loaded until it is called by some external factor. For example, in Firefox, users are given the option to choose a stylesheet in the settings (View &gt; Page Style). In other browsers, this option is non existent.</p> <p>Therefore, to make the alternate stylesheet useful to users, we have to use JavaScript to enable them to switch between the stylesheets. Paul Sowden created a javascript library to help you do this. It's called <a href="https://d.alistapart.com/alternate/styleswitcher.js">stylesheetswitcher.js</a>, and allows you to create something like this -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/aFNGqkJsoz-320.avif 320w"><source type="image/gif" srcset="https://bitsofco.de/img/aFNGqkJsoz-320.gif 320w"><img alt="Style Switcher Example" loading="lazy" decoding="async" src="https://bitsofco.de/img/aFNGqkJsoz-320.webp" width="320" height="291"></picture></p> <p>By just writing this -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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"><span class="token function">setActiveStyleSheet</span><span class="token punctuation">(</span><span class="token string">'Light Theme'</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Light Theme<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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"><span class="token function">setActiveStyleSheet</span><span class="token punctuation">(</span><span class="token string">'Dark Theme'</span><span class="token punctuation">)</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Dark Theme<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span></code></pre> <p>Although this is not something that is very widely used, especially for regular websites, it does make for an interesting design if used properly.</p> <h3 id="alternate-stylesheets-dependent-on-media" tabindex="-1">Alternate Stylesheets dependent on Media <a class="header-anchor" href="https://bitsofco.de/working-with-document-alternates/">#</a></h3> <p>If we want to provide different stylesheets dependent on the media type or screen size, the <code>rel=&quot;alternate&quot;</code> value <strong>should not</strong> be used. As mentioned above, this will cause the alternate stylesheet to not be loaded at all.</p> <p>Instead, we only need to define the media attribute, and let the browser determine which stylesheet should be used.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>stylesheet<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>screen.css<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>screen<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><br><span class="token comment">&lt;!-- For smaller devices --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>stylesheet<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>screen-small.css<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>only screen and (max-device-width: 480px)<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><br><span class="token comment">&lt;!-- For print --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>stylesheet<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>print.css<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>print<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>This way, all stylesheets are loaded, but which stylesheet is active will be dependent on the relevant media.</p> <h2 id="alternate-languages" tabindex="-1">Alternate Languages <a class="header-anchor" href="https://bitsofco.de/working-with-document-alternates/">#</a></h2> <p>It is becoming more common to have several versions of a site translated in different languages. Even with tools like Google translate, it is more accurate to provide a self-translated version. So, for example, you might have <code>https://example.com</code> as the main version of the site in your native language and <code>https://example.com/fr</code> for a version of the same website translated in French.</p> <p>To specify what different language versions of a site are available, we use the <code>hreflang</code> attribute paired with <code>rel=&quot;alternate&quot;</code>.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>alternate<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>https://example.com/fr<span class="token punctuation">"</span></span> <span class="token attr-name">hreflang</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></code></pre> <p>The hreflang attribute must be set to a valid <a href="https://www.w3.org/International/articles/language-tags/">BCP 47 language tag</a>, optionally suffixed with a <a href="https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">region code</a> and/or <a href="https://unicode.org/iso15924/iso15924-codes.html">script variation</a>. For example -</p> <table> <thead> <tr> <th>hreflang</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>de</td> <td>German language, no specified region</td> </tr> <tr> <td>de-es</td> <td>German language, users in Spain</td> </tr> <tr> <td>de-fr-latn</td> <td>German language, users in France, Latin script</td> </tr> <tr> <td>fr-be</td> <td>French language, users in Belgium</td> </tr> <tr> <td>zh-Hant</td> <td>Chinese language, traditional script</td> </tr> <tr> <td>zh-Hans</td> <td>Chinese language, simplified script</td> </tr> <tr> <td>en-gb</td> <td>English language, users in Great Britain</td> </tr> <tr> <td>x-default</td> <td>Used if there are multiple languages on the same page</td> </tr> </tbody> </table> <p>The purpose of the tag is not necessarily to tell the browser what language the page is in. In fact, browsers are not supposed to consider it authoritative. What it is useful for is in search results. <a href="https://support.google.com/webmasters/answer/189077?hl=en">Google uses it</a> to serve the correct regional URL in search results based on where the user is searching from.</p> <p>However, you should be careful not to go to specific. <a href="https://www.w3.org/TR/2015/WD-ltli-20150423/">The W3C recomends</a> that the script subtags should only be used to distinguish varieties of a language that would normally vary.</p> <h2 id="syndication-feed" tabindex="-1">Syndication Feed <a class="header-anchor" href="https://bitsofco.de/working-with-document-alternates/">#</a></h2> <p>The rss/atom feed is essentially the same content of the site written in a format that can be read by syndication readers. Therefore, the alternate value is used to link to the document.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token comment">&lt;!-- RSS Feed --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>alternate<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>application/rss+xml<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>path/to/rss<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><br><br><span class="token comment">&lt;!-- Atom Feed --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>alternate<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>application/atom+xml<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>path/to/data.xml<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <h2 id="alternate-format" tabindex="-1">Alternate Format <a class="header-anchor" href="https://bitsofco.de/working-with-document-alternates/">#</a></h2> <p>If a page on the site is also provided in another format, the alternate link tag can be used. The format of the document, in a valid MIME type, must also be provided. For example, if the content of a page is also available in PDF, we could write -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>alternate<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>application/pdf<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>path/to/application.pdf<span class="token punctuation">"</span></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>PDF Version<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> FormHack.io - A Hackable CSS Form Reset 2015-04-28T00:00:00Z https://bitsofco.de/formhack-css-form-reset/ <p>Dealing with forms can be a <em>bit</em> of a hassle. No matter how thorough I think I'm are being, if I am styling form elements from scratch, I am likely to miss out some vendor prefix that causes the field to be styled slightly differently on some browser.</p> <p>This has always really frustrated me, so I decided to create a CSS reset for form elements called <a href="https://formhack.io">FormHack</a>. FormHack is different from other CSS resets for two reasons -</p> <ol> <li>Its written in SASS/SCSS</li> <li>Its &quot;hackable&quot; - it isn't just a reset file, it allows you to make custom designs</li> </ol> <p>FormHack works by providing a few variables that allow you to change some common attributes such as border-radius, input height/width, and colours. By changing these attributes, you get a form that is consistent across all major browsers without having to write it all by hand.</p> <h2 id="how-it-works" tabindex="-1">How it works <a class="header-anchor" href="https://bitsofco.de/formhack-css-form-reset/">#</a></h2> <p>FormHack is based on <a href="https://sass-lang.com">SASS</a>, which, if you don't already know, is a CSS pre-processor. SASS allows you to add some awesome functionality to regular CSS such as creating variables and writing reusable functions.</p> <p>With SASS variables I was able to create a settings section of the file. In this, I tried to extract the common attributes that I would vary from form to form and put them into these variables. This enables me to make significant design changes between forms by only changing those variables.</p> <p>Here is the default settings example -</p> <pre class="language-scss" tabindex="0"><code class="language-scss"><span class="token comment">// Font</span><br><span class="token property"><span class="token variable">$fh-font-family</span></span><span class="token punctuation">:</span> <span class="token string">'Raleway'</span><span class="token punctuation">,</span> sans-serif<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-font-size</span></span><span class="token punctuation">:</span> 16px<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-font-color</span></span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>40<span class="token punctuation">,</span> 40<span class="token punctuation">,</span> 40<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Borders</span><br><span class="token property"><span class="token variable">$fh-border-radius</span></span><span class="token punctuation">:</span> 5px<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-border-width</span></span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-border-style</span></span><span class="token punctuation">:</span> solid<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-border-color</span></span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>200<span class="token punctuation">,</span> 200<span class="token punctuation">,</span> 200<span class="token punctuation">)</span><span class="token punctuation">;</span><br><br><span class="token comment">// Inputs, Textareas, Select, Option</span><br><span class="token property"><span class="token variable">$fh-input-height</span></span><span class="token punctuation">:</span> 40px<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-input-width</span></span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-input-max-width</span></span><span class="token punctuation">:</span> 400px<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-input-bg-color</span></span><span class="token punctuation">:</span> #fff<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-focus-bg-color</span></span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>220<span class="token punctuation">,</span> 220<span class="token punctuation">,</span> 220<span class="token punctuation">)</span><span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-focus-border-color</span></span><span class="token punctuation">:</span> <span class="token variable">$fh-border-color</span><span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-focus-font-color</span></span><span class="token punctuation">:</span> <span class="token variable">$fh-font-color</span><span class="token punctuation">;</span><br><br><span class="token comment">// Select Vendor Styling</span><br><span class="token property"><span class="token variable">$fh-allow-vendor-styling</span></span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">;</span><br><br><span class="token comment">// Fieldset &amp; Legend Styling</span><br><span class="token property"><span class="token variable">$fh-fieldset-bare</span></span><span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">;</span><br><br><span class="token comment">// Buttons &amp; Input Submits</span><br><span class="token property"><span class="token variable">$fh-button-height</span></span><span class="token punctuation">:</span> 40px<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-button-width</span></span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-button-max-width</span></span><span class="token punctuation">:</span> 200px<span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-button-font-color</span></span><span class="token punctuation">:</span> <span class="token variable">$fh-font-color</span><span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-button-bg-color</span></span><span class="token punctuation">:</span> <span class="token variable">$fh-focus-bg-color</span><span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-button-hover-bg-color</span></span><span class="token punctuation">:</span> <span class="token variable">$fh-border-color</span><span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-buton-hover-font-color</span></span><span class="token punctuation">:</span> <span class="token variable">$fh-font-color</span><span class="token punctuation">;</span><br><br><span class="token comment">// Layout</span><br><span class="token property"><span class="token variable">$fh-centered</span></span><span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">;</span><br><span class="token property"><span class="token variable">$fh-display</span></span><span class="token punctuation">:</span> block<span class="token punctuation">;</span></code></pre> <p>You can see how these default settings render across different browsers from the <a href="https://formhack.io">FormHack website</a>.</p> <h2 id="cross-browser-frustrations" tabindex="-1">Cross Browser Frustrations <a class="header-anchor" href="https://bitsofco.de/formhack-css-form-reset/">#</a></h2> <p>Through creating this, I got into to all the nitty gritty differences between browsers when displaying form elements. Some of them were understandable, others not so much. Here are a few particularly strange nuances.</p> <h3 id="inset-shadow-on-ios" tabindex="-1">Inset Shadow on iOS <a class="header-anchor" href="https://bitsofco.de/formhack-css-form-reset/">#</a></h3> <p>On almost all input and textarea elements, the Safari browser adds an inner shadow like so -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/B_QEecNCnT-350.avif 350w"><source type="image/webp" srcset="https://bitsofco.de/img/B_QEecNCnT-350.webp 350w"><img alt="Input Type Text Inner Shadow" loading="lazy" decoding="async" src="https://bitsofco.de/img/B_QEecNCnT-350.png" width="350" height="133"></picture></p> <p>Although it is almost unnoticeable, it really bugged me because it remains even if you define the border around the element or set the inset shadow to none. Even though this only happens on Safari on iOS, the only way to remove this is to add -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">input,<br>textarea</span> <span class="token punctuation">{</span><br> <span class="token property">-webkit-appearance</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <h3 id="input-type-file" tabindex="-1">Input Type File <a class="header-anchor" href="https://bitsofco.de/formhack-css-form-reset/">#</a></h3> <p>The file input type is probably the least customisable element across browsers. You can pretty much only style the background colour and border around it. However, if you set a <code>height</code> attribute on the element, Firefox styles it in a weird way -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/y0HuNEzHJG-350.avif 350w"><source type="image/webp" srcset="https://bitsofco.de/img/y0HuNEzHJG-350.webp 350w"><img alt="Input Type File With Height" loading="lazy" decoding="async" src="https://bitsofco.de/img/y0HuNEzHJG-350.png" width="350" height="126"></picture></p> <p>Strangely, if you set a <code>min-height</code> attribute instead to the same value, it goes back to normal -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/6eB5-_JnM6-350.avif 350w"><source type="image/webp" srcset="https://bitsofco.de/img/6eB5-_JnM6-350.webp 350w"><img alt="Input Type File With Min-Height" loading="lazy" decoding="async" src="https://bitsofco.de/img/6eB5-_JnM6-350.png" width="350" height="105"></picture></p> <h3 id="select" tabindex="-1">Select <a class="header-anchor" href="https://bitsofco.de/formhack-css-form-reset/">#</a></h3> <p>Styling the select element can be difficult. In some browsers, you have complete control over styling the border, border-radius, and background-color. In others, it is not so easy.</p> <p>For example, the Safari browser by default adds a gradient background and curved borders on the element, which overrides any styling you set.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/xtxpczfuRf-461.avif 461w"><source type="image/webp" srcset="https://bitsofco.de/img/xtxpczfuRf-461.webp 461w"><img alt="Select with Vendor Styling" loading="lazy" decoding="async" src="https://bitsofco.de/img/xtxpczfuRf-461.png" width="461" height="90"></picture></p> <p>This can only be removed by setting -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token selector">select</span> <span class="token punctuation">{</span><br> <span class="token property">-webkit-appearance</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>However, doing so also gets rid of the arrow, which lets you know that it is a dropdown menu -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/WL6nwXhHZL-500.avif 500w"><source type="image/webp" srcset="https://bitsofco.de/img/WL6nwXhHZL-500.webp 500w"><img alt="Select without Vendor Styling" loading="lazy" decoding="async" src="https://bitsofco.de/img/WL6nwXhHZL-500.png" width="500" height="101"></picture></p> <p>It also applies to the Chrome and Opera browsers, in which the styling is not so obtrusive. I haven't completed decided where I stand on whether it is a good idea for remove the arrows for accessibility reasons. Most of the time I opt to keep the arrows, which I think looks great on Chrome, but particularly ugly on iOS Safari. In FormHack, you can choose whether to keep the vendor styling in the settings section.</p> <p>Those are just a few of the issues that come with styling forms. If you use <a href="https://formhack.io">FormHack</a> or have any feedback on it, do let me know! You can leave a comment below or tweet me.</p> All About Favicons (And Touch Icons) 2015-04-21T00:00:00Z https://bitsofco.de/all-about-favicons-and-touch-icons/ <p>This week I decided to look into the proper ways to use site favicons (and by extension, mobile &quot;touch icons&quot;) across browsers. Although there are already quite a few articles on this topic, I decided to write this post as a way for me to bring together everything I have researched and present it in an easy to understand format for myself and, hopefully, you.</p> <h2 id="the-basics" tabindex="-1">The Basics <a class="header-anchor" href="https://bitsofco.de/all-about-favicons-and-touch-icons/">#</a></h2> <p>The favicon (favourite icon) is an image used by browsers to represent a web page. It is typically 16x16 pixels, but varying and larger sizes are now more frequently required due to browsers using them in a growing number of ways -</p> <ul> <li>The address bar</li> <li>The links bar</li> <li>Bookmarks</li> <li>Tabs</li> <li>Desktop icon</li> </ul> <p>If we do not specify where to find the favicon, all major browsers (as early as IE5) will by default search for a file named &quot;favicon.ico&quot; in the website's root directory. This means that we technically do not need to declare anything else to have a working favicon.</p> <p>However, using the ico format can be limiting. It doesn't support transparency, and isn't the most optimized for the web. Nowadays, we are able to use a wider range of formats including <a href="https://caniuse.com/#feat=link-icon-png">png</a>, gif, jpeg, and, in limited circumstances, <a href="https://caniuse.com/#feat=link-icon-svg">svg</a>.</p> <h2 id="favicon-declaration-and-the-link-tag" tabindex="-1">Favicon Declaration &amp; the Link Tag <a class="header-anchor" href="https://bitsofco.de/all-about-favicons-and-touch-icons/">#</a></h2> <p>If deviating from the default, favicons can be declared using the <code>&lt;link&gt;</code> tag. The tag accepts the following attributes -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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><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><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><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><span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <h3 id="rel" tabindex="-1">Rel <a class="header-anchor" href="https://bitsofco.de/all-about-favicons-and-touch-icons/">#</a></h3> <p>The rel attribute is used to declare the relationship between the html document and the linked item. We use this for linking stylesheets among other things. With regards to favicons, the <a href="https://www.w3.org/html/wg/drafts/html/master/semantics.html#rel-icon">official HTML documentation</a> states that this attribute should be set to as -</p> <pre class="language-html" tabindex="0"><code class="language-html"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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 punctuation">></span></span></code></pre> <p>However, as you may have seen before, the attribute is sometimes written as -</p> <pre class="language-html" tabindex="0"><code class="language-html"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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>shortcut icon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>This is because some older browsers (<a href="https://blogs.msdn.com/b/ieinternals/archive/2013/09/08/internet-explorer-favicons-png-link-rel-icon-caching.aspx">IE8 and below</a>) require it to be written this way, and will ignore the entire tag if otherwise. For this reason, although shortcut icon is not formally part of HTML5, it is still recognised and modern browsers will accept it. However, for reasons I will explain, you may only need to use the official method in practice.</p> <h3 id="type" tabindex="-1">Type <a class="header-anchor" href="https://bitsofco.de/all-about-favicons-and-touch-icons/">#</a></h3> <p>The type attribute specifies the <a href="https://www.iana.org/assignments/media-types/media-types.xhtml#image">MIME type format</a> of the item being linked. For example an ico file is of the type <code>image/x-icon</code> and a png file <code>image/png</code>.</p> <p><a href="https://www.w3.org/html/wg/drafts/html/master/semantics.html#attr-link-type">According to the W3C</a>, specifying the type is &quot;purely advisory&quot;:</p> <blockquote> <p>&quot;The type attribute is used as a hint to user agents so that they can avoid fetching resources they do not support... User agents must not consider the type attribute authoritative&quot;</p> </blockquote> <p>Despite this, IE 9 and 10 require the type attribute to be specified. In these versions, although the need to define the rel attribute as <code>shortcut icon</code> was removed, they instead <a href="https://blogs.msdn.com/b/ieinternals/archive/2011/02/11/ie9-release-candidate-minor-changes-list.aspx">added a need to specify the media type</a> as <code>image/x-icon</code>.</p> <p>Luckily, that is not the case for IE 11, which, like other modern browsers, does not require a media type to be specified.</p> <h3 id="sizes" tabindex="-1">Sizes <a class="header-anchor" href="https://bitsofco.de/all-about-favicons-and-touch-icons/">#</a></h3> <p>The sizes attribute is used to declare what size you want the particular linked file to be used for. As different sizes are used for different purposes, you can serve optimised files for each of these purposes. This is especially important when using png files, as they are not scalable. Here is a great <a href="https://github.com/audreyr/favicon-cheat-sheet">cheat sheet</a> which shows what sizes you need and what they are used for.</p> <h3 id="href" tabindex="-1">Href <a class="header-anchor" href="https://bitsofco.de/all-about-favicons-and-touch-icons/">#</a></h3> <p>This specifies the location of the favicon.</p> <h3 id="putting-it-together" tabindex="-1">Putting it together <a class="header-anchor" href="https://bitsofco.de/all-about-favicons-and-touch-icons/">#</a></h3> <table> <thead> <tr> <th>Browser</th> <th>Link “rel”/ “type”</th> <th>Accepted Formats</th> </tr> </thead> <tbody> <tr> <td>IE 8 and below</td> <td>link rel=”shortcut icon”</td> <td>ico</td> </tr> <tr> <td>IE 9, IE 10</td> <td>link rel=”icon” type=”image/x-icon” orlink rel=”shortcut icon”</td> <td>ico</td> </tr> <tr> <td>IE 11</td> <td>link rel=”icon”</td> <td>ico, png, gif</td> </tr> <tr> <td>Chrome</td> <td>link rel=”icon”</td> <td>ico, png, gif</td> </tr> <tr> <td>Firefox</td> <td>link rel=”icon”</td> <td>ico, png, gif, <a href="https://caniuse.com/#feat=link-icon-svg">svg*</a></td> </tr> <tr> <td>Safari</td> <td>link rel=”icon”</td> <td>ico, png, gif</td> </tr> <tr> <td>Opera</td> <td>link rel=”icon”</td> <td>ico, png, gif</td> </tr> </tbody> </table> <p>Based on these differences, it may seem like the best method would be to declare two versions - the png files using the modern method and an ico file using the IE8 method.</p> <p><strong>However</strong>, it appears that some modern browsers will choose a linked ico file over a linked png file, regardless of the order they are placed. This means that, if we try to accomodate for the older IE browsers, the modern browsers may be served the wrong format.</p> <p>Because of this, the best solution may be to only declare the png format files, and let older browsers use the default as a fallback.</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token comment">&lt;!-- For IE 10 and below --></span><br><span class="token comment">&lt;!-- No link, just place a file called favicon.ico in the root directory --></span><br><br><span class="token comment">&lt;!-- For IE 11, Chrome, Firefox, Safari, Opera --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/favicon-16.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>16x16<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 punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/favicon-32.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">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 punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/favicon-48.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">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 punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>path/to/favicon-62.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>62x62<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 punctuation">></span></span></code></pre> <h2 id="touch-icons-for-mobile-devices" tabindex="-1">Touch Icons for Mobile Devices <a class="header-anchor" href="https://bitsofco.de/all-about-favicons-and-touch-icons/">#</a></h2> <p>Some mobile browsers allow users to bookmark the web page to their home screen. We can provide a special icon to be used in these cases, similar to a native application icon.</p> <p>The way we define what image to use as this &quot;touch icon&quot; is, of course, dependent on the browser/device.</p> <table> <thead> <tr> <th>Device / Browser</th> <th>Link “rel”</th> <th>Sizes</th> </tr> </thead> <tbody> <tr> <td>Apple / Safari</td> <td>link rel=”apple-touch-icon” orlink rel=”apple-touch-icon-precomposed”</td> <td>76x76 - iPad 2 and iPad mini120x120 - iPhone 4s, 5, 6152x152 - iPad (retina)180x180 - iPhone 6 Plus</td> </tr> <tr> <td>Apple / Opera Coast</td> <td>link rel=”icon”<small>(<a href="https://dev.opera.com/articles/opera-coast/">Will also accept Safari and Windows formats</a>)</small></td> <td>228x228</td> </tr> <tr> <td>Android / Chrome</td> <td>link rel=”icon”<small>(<a href="https://developer.chrome.com/multidevice/android/installtohomescreen">Will, for a limited time, also accept Safari format</a>)</small></td> <td>192x192</td> </tr> </tbody> </table> <p>For Windows, these icons are defined using the meta tag instead.</p> <p>For Windows 8 / IE 10 -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>pinned-tile.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">&lt;</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-TileColor<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#009900<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></code></pre> <p>For Windows 8.1 / IE 11 -</p> <pre class="language-html" tabindex="0"><code class="language-html"><span class="token comment">&lt;!-- In &lt;head> --></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</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-config<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ieconfig.xml<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span><br><br><span class="token comment">&lt;!-- In ieconfig.xml --></span><br><span class="token prolog">&lt;?xml version="1.0" encoding="utf-8"?></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>browserconfig</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>msapplication</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tile</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>square70x70logo</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>images/smalltile.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">&lt;</span>square150x150logo</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>images/mediumtile.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">&lt;</span>wide310x150logo</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>images/widetile.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">&lt;</span>square310x310logo</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>images/largetile.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">&lt;</span>TileColor</span><span class="token punctuation">></span></span>#009900<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TileColor</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tile</span><span class="token punctuation">></span></span><br> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>msapplication</span><span class="token punctuation">></span></span><br><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>browserconfig</span><span class="token punctuation">></span></span></code></pre> <p>That's (pretty much) it! For such a small part of a site, favicons require way too many parts. For very basic sites, I will probably just stick with using the ico format unless I need something fancier. I just can't wait until SVG favicons become a thing, although we are still a long way away!</p> Payback - An Instagram Based Web App 2015-04-14T00:00:00Z https://bitsofco.de/payback-an-instagram-based-web-app/ <p>This was a really fun week for me. Apart from the fact that my article made it to the front page of Designer News and on Sidebar (😱), I decided to learn more about working with APIs and ended up building two tiny web apps based on Instagram's API.</p> <p><strong>Tagr</strong> is an app that lets you search for instagram posts based on a tag you submit (<a href="https://github.com/ireade/tagr">View on Github</a>).</p> <p><strong>Payback</strong> is an app that lets you find the people you follow on instagram that don't follow you back ( <a href="https://github.com/ireade/payback">View on Github</a>). As this was the more complicated application, I thought I'd share with you my process below.</p> <h2 id="payback-project-overview" tabindex="-1">Payback Project Overview <a class="header-anchor" href="https://bitsofco.de/payback-an-instagram-based-web-app/">#</a></h2> <p>In order to get the relevant information about the user, I had to get authorisation using the OAuth 2.0 protocol. As this was a purely javascript app with no server component, I had to use the <a href="https://instagram.com/developer/authentication/">Implicit Authentication method</a>, which was very simple to implement (although less secure). All I had to do was send the user to the following url -</p> <pre><code>&lt;a href=&quot;https://api.instagram.com/oauth/authorize/?client_id=MY-CLIENT-ID&amp;redirect_uri=MY-REDIRECT-URL&amp;response_type=token&quot;&gt;Authorize&lt;/a&gt; </code></pre> <p>When the user was redirected to my redirect url, which was defined when I <a href="https://instagram.com/developer/clients/manage/">registered my application</a>, the access token was passed with the url. With the information returned, I was able to perform the relationship GET requests.</p> <p>I broke the process down into three steps, represented by three GET requests -</p> <ul> <li>GET the list of people that the user follows, the &quot;follows&quot;</li> <li>Loop through each of these &quot;follows&quot; and GET their relationship with the user</li> <li>If one of these &quot;follows&quot; does not follow the user back, GET their details and add them to the list displayed on the page</li> </ul> <p>Here is my app.js file -</p> <pre><code>$(document).ready(function() { var accessToken = window.location.hash.split(&quot;=&quot;)[1]; var userID = accessToken.split(&quot;.&quot;)[0]; var unfollowersCount = 0; var followsRequestUrl = 'https://api.instagram.com/v1/users/'+userID+'/follows?access_token='+accessToken+'&amp;count=500&amp;callback=?'; $.getJSON(followsRequestUrl, {}, function(response) { $.each(response.data, function(index, value){ // The ID of the person you follow var followsId = value.id; // Get the relationship status of the person you are following var relationshipRequestUrl = 'https://api.instagram.com/v1/users/'+followsId+'/relationship?access_token='+accessToken+'&amp;callback=?'; $.getJSON(relationshipRequestUrl, {}, function(relationshipResponse) { var relationshipStatus = relationshipResponse.data.incoming_status; // If the person does not follow you if (relationshipStatus == &quot;none&quot;) { unfollowersCount++; // Get the details of the person var unfollowersDetailsRequestUrl = 'https://api.instagram.com/v1/users/'+followsId+'/?access_token='+accessToken+'&amp;callback=?'; $.getJSON(unfollowersDetailsRequestUrl, {}, function(unfollowersDataResponse) { var username = unfollowersDataResponse.data.username; var profile = unfollowersDataResponse.data.profile_picture; $('#payback').append('&lt;li class=&quot;unfollower&quot; data-unfollowerId=&quot;'+followsId+'&quot;&gt;&lt;a href=&quot;https://www.instagram.com/'+username+'&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;'+profile+'&quot; alt=&quot;'+username+'&quot;&gt;&lt;span&gt;'+username+'&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;'); $('#unfollowers-count').html(unfollowersCount+&quot; people don't follow back&quot;); }) // end unfollowersDetailsRequest } // end if relationshipStatus }) // end relationshipRequest }) // end .each follows }) // end followsRequest }) // end document.ready </code></pre> <p>That's it! Although this was a simple project, I learned something and the process was fun. As always, if you have any suggestions on how I can improve, please leave a comment below.</p> <h2 id="some-obvious-advice" tabindex="-1">Some Obvious Advice <a class="header-anchor" href="https://bitsofco.de/payback-an-instagram-based-web-app/">#</a></h2> <blockquote> <p>Just read the documentation. It's easier.</p> </blockquote> <p>One thing I learned through this process was to always just read the documentation. There were a few times I got a bit lost and went googling for ages, when my problem could have easily been solved if I just read the relevant Instagram doc.</p> <p>For next week, I'm researching favicons. Stay tuned!</p> CSS Font Sizing 2015-04-04T00:00:00Z https://bitsofco.de/css-font-sizing/ <p>Following on from <a href="https://bitsofco.de/responsive-design-viewport">last week's article</a> in which I briefly discussed css pixel sizing, I decided to look into the different ways you can declare your font size with CSS.</p> <h2 id="meet-the-units" tabindex="-1">Meet the Units <a class="header-anchor" href="https://bitsofco.de/css-font-sizing/">#</a></h2> <p>There are several different ways you can declare the size of a font in CSS. Broadly speaking, the units fall into one of two categories - absolute and relative.</p> <p><strong>Absolute units</strong> are fixed and (mostly) relate to some physical measurement. Once they are declared, their size cannot be altered by changing the font size of some other element.</p> <p><strong>Relative units</strong> do not have an objective measurement. Instead, their actual size is determined by the size of a parent element. This means that their size <em>can</em> be altered by changing the sizing of that dependent element.</p> <p>Here is a brief overview of some of the units -</p> <table> <thead> <tr> <th>Unit</th> <th>Type</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>px</td> <td>Absolute</td> <td>1 “<a href="https://bitsofco.de/responsive-design-viewport">viewport pixel</a>”</td> </tr> <tr> <td>pt</td> <td>Absolute</td> <td>1 point is 1/72 of an inch</td> </tr> <tr> <td>pc</td> <td>Absolute</td> <td>1 pica is equal to 12 points</td> </tr> <tr> <td>%</td> <td>Relative</td> <td>Relative to the parent element’s font size</td> </tr> <tr> <td>em</td> <td>Relative</td> <td>Relative to the parent element’s font size</td> </tr> <tr> <td>rem</td> <td>Relative</td> <td>(root em) Relative to the <code>html</code> font size</td> </tr> <tr> <td>keyword</td> <td>Relative</td> <td>xx-small, x-small, small, medium, large, x-large, xx-large</td> </tr> <tr> <td>vw</td> <td>Relative</td> <td>1/100th of the width of the viewport</td> </tr> <tr> <td>vh</td> <td>Relative</td> <td>1/100th of the height of the viewport</td> </tr> <tr> <td>vmin</td> <td>Relative</td> <td>1/100th of the viewport’s smaller dimension (height or width)</td> </tr> <tr> <td>vmax</td> <td>Relative</td> <td>1/100th of the viewport’s larger dimension (height or width)</td> </tr> </tbody> </table> <p>You can see an <a href="https://www.w3schools.com/cssref/css_units.asp">exhaustive list of units here</a>, but I will focus on what I think are the most relevant units - px, pt, %, em, rem, and vw.</p> <h2 id="what-s-the-difference" tabindex="-1">What's the difference? <a class="header-anchor" href="https://bitsofco.de/css-font-sizing/">#</a></h2> <p>The difference between these units can be difficult to understand conceptually, so it is best to demonstrate their differences with practical examples.</p> <h3 id="example-1-default-settings" tabindex="-1">Example 1 - Default Settings <a class="header-anchor" href="https://bitsofco.de/css-font-sizing/">#</a></h3> <p>In a blank HTML document, without you declaring any font sizing, there are default settings used. In most browsers, the default font size for the <code>html</code> and <code>body</code> tags is 100%. This equates to the following -</p> <blockquote> <p>100% = 1em = 1rem = 16px = 12pt</p> </blockquote> <p>This means that if you set the font size of one <code>&lt;p&gt;</code> to 100% and another <code>&lt;p&gt;</code> to 16px, they will render as the same size on screen. You can see this demonstrated here -</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/HyWQV7Eofe-585.avif 585w"><source type="image/webp" srcset="https://bitsofco.de/img/HyWQV7Eofe-585.webp 585w"><img alt="Default Font Settings" loading="lazy" decoding="async" src="https://bitsofco.de/img/HyWQV7Eofe-585.png" width="585" height="337"></picture></p> <h3 id="example-2-absolute-vs-relative-units" tabindex="-1">Example 2 - Absolute vs Relative Units <a class="header-anchor" href="https://bitsofco.de/css-font-sizing/">#</a></h3> <p>The difference between absolute and relative units can be highlighted by altering the <code>html</code> font size. If we set <code>html { font-size: 200% }</code>, this will affects only the <code>&lt;p&gt;</code>s with relative font size units.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/RjuFm25S9q-600.avif 600w"><source type="image/webp" srcset="https://bitsofco.de/img/RjuFm25S9q-600.webp 600w"><img alt="Absolute vs relative Units" loading="lazy" decoding="async" src="https://bitsofco.de/img/RjuFm25S9q-600.png" width="600" height="624"></picture></p> <p>This is a key advantage of using relative units. With the ability to scale the font sizes so easily, you can create a truly responsive site, just by altering the <code>html</code> font size. <a href="https://jsfiddle.net/25gCt/4/">Max Luster has a great example of this here</a>.</p> <h3 id="example-3-rem-vs-em-and" tabindex="-1">Example 3 - rem vs em (and %) <a class="header-anchor" href="https://bitsofco.de/css-font-sizing/">#</a></h3> <p>The Em (and %) unit works by calculating the current font size based on the parent element's font size. For example -</p> <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> 100% <span class="token comment">/* =16px */</span><br><span class="token punctuation">}</span><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> 2em<span class="token punctuation">;</span> <span class="token comment">/* =32px */</span><br><span class="token punctuation">}</span><br><span class="token selector">p</span> <span class="token punctuation">{</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 1em<span class="token punctuation">;</span> <span class="token comment">/* =32px */</span><br> <span class="token comment">/* font-size: 0.5em; =16px */</span><br><span class="token punctuation">}</span></code></pre> <p>Because <code>p</code> inherits from <code>body</code>, which inherits from <code>html</code>, the paragraphs set in em and % units turn out twice the size we may have intended.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/SvWyblPsHp-591.avif 591w"><source type="image/webp" srcset="https://bitsofco.de/img/SvWyblPsHp-591.webp 591w"><img alt="Rem vs Em" loading="lazy" decoding="async" src="https://bitsofco.de/img/SvWyblPsHp-591.png" width="591" height="443"></picture></p> <p>When you use em units for an element, you have to take into account the font size of all the parent elements. As you can see, this can get complicated and messy pretty quickly.</p> <p>The solution to this problem is rem. rem is calculated based on the <code>html</code> font size only, not the parent element. For example -</p> <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> 100% <span class="token comment">/* =16px */</span><br><span class="token punctuation">}</span><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> 2rem<span class="token punctuation">;</span> <span class="token comment">/* =32px */</span><br><span class="token punctuation">}</span><br><span class="token selector">p</span> <span class="token punctuation">{</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span> <span class="token comment">/* =16px */</span><br><span class="token punctuation">}</span></code></pre> <p>Using rem allows you to have the scaling abilities of em and %, but without having to deal with nesting issues.</p> <h3 id="example-4-viewport-width-sizing" tabindex="-1">Example 4 - Viewport width sizing <a class="header-anchor" href="https://bitsofco.de/css-font-sizing/">#</a></h3> <p>The vw unit, <a href="https://dev.w3.org/csswg/css-values-3/#viewport-relative-lengths">a new CSS3 unit</a> uses the viewport width to calculate the font size. This allows for a more fluid responsive font sizing.</p> <p>Although this seems like a great unit for responsive design, I personally am not a fan of it. It doesn't give me enough control over the size of the font, which almost always works out to be too big or too small.</p> <h2 id="my-method" tabindex="-1">My Method <a class="header-anchor" href="https://bitsofco.de/css-font-sizing/">#</a></h2> <p>Until I did this research, I was just using pixels to set my font sizes. This is because, nowadays, most browsers allow the user to zoom in if the text is too small for them, and so there is no accessibility issue.</p> <p>However, I have found this method quite limiting in terms of ability to scale. Although my font sizing looks good on medium and small screens, it could be better optimised for larger ones. Even if users have the option to zoom in, that isn't something we want them to need to do.</p> <p>My solution is therefore to work with rem (using pixels as a fallback).</p> <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> <span class="token comment">/* sets the base font to 10px for easier math */</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> 16px<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 comment">/* sets the default sizing to make sure nothing is actually 10px */</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">font-size</span><span class="token punctuation">:</span> 32px<span class="token punctuation">;</span><br> <span class="token property">font-size</span><span class="token punctuation">:</span> 3.2rem<span class="token punctuation">;</span><br><span class="token punctuation">}</span></code></pre> <p>This allows me to scale up my font sizes simply by writing -</p> <pre class="language-css" tabindex="0"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 1280px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span><br> <span class="token selector">html</span> <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 punctuation">}</span><br><span class="token punctuation">}</span></code></pre> <p>This method uses pixels as a fallback unit because <a href="https://caniuse.com/#feat=rem">rem isn't supported on IE8 and below</a>. One issue this creates is when I change the base font size for scalability like above, this doesn't apply for the fallback font size. However, I don't consider this a huge issue because the ability to scale for larger devices is more of an extra feature than it is a core one.</p> <p>If you have any ideas of how I can improve on this, let me know in the comments. I might also write a SCSS mixin so I don't have to input both the fallback and the rem units.</p> Responsive Design With Viewport Control 2015-03-31T00:00:00Z https://bitsofco.de/responsive-design-viewport/ <p>When it comes to implementing responsive design, css media queries seem to get all the attention. Although admittedly all the leg work is done with media queries, a site is not responsive without viewport control.</p> <p>When we want to alter the layout of a site depending on screen size, we typically write something like this:</p> <pre><code>@media screen and (min-width: 960px) { body { background-color: green; } } @media screen and (max-width: 960px) and (min-width: 500px) { body { background-color: pink; } } @media screen and (max-width: 500px) { body { background-color: yellow; } } </code></pre> <p>When testing the responsiveness of the site by resizing the browser, everything seems to work fine.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/EtEaQwhzVz-480.avif 480w"><source type="image/gif" srcset="https://bitsofco.de/img/EtEaQwhzVz-480.gif 480w"><img alt="Viewport Resizing" loading="lazy" decoding="async" src="https://bitsofco.de/img/EtEaQwhzVz-480.webp" width="480" height="360"></picture></p> <h2 id="the-problem" tabindex="-1">The problem <a class="header-anchor" href="https://bitsofco.de/responsive-design-viewport/">#</a></h2> <p>The problem arises when viewing the responsive site on a mobile device.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/lcvBPWYC2p-250.avif 250w"><source type="image/webp" srcset="https://bitsofco.de/img/lcvBPWYC2p-250.webp 250w"><img alt="Viewport Resizing" loading="lazy" decoding="async" src="https://bitsofco.de/img/lcvBPWYC2p-250.png" width="250" height="445"></picture> (iPhone 6, No viewport control)</p> <p>As you can see, even though the page is being viewed from an iPhone 6, the page is rendering as though in the desktop view. This is caused by a discrepancy between the size of the <em>device</em> and the size of the <em>viewport</em>. To understand this, three distinctions need to be made.</p> <ul> <li> <p><strong>Device pixel</strong> - The amount of pixels on the physical display. An iPhone 6, for example, is 375x667 pixels. (Side note: there are two types of device pixels - hardware pixels and device independent pixels - but this distinction is not pertinent).</p> </li> <li> <p><strong>CSS pixel</strong> - This is an abstract unit we use in CSS declarations. Taken alone, 1 CSS pixel does not have an objective size. Instead, how 1 CSS pixel renders on the webpage is determined by the viewport.</p> </li> <li> <p><strong>Viewport</strong> - This determines the pixel density of the page. A viewport width of, for example, 750px, would mean that 750 CSS pixels would span the width of the screen (with a 1:1 scale).</p> </li> </ul> <blockquote> <p>The viewport is &quot;device agnostic&quot;, it doesn't inherently know how big our device screen actually is.</p> </blockquote> <p>By default, most browsers set the viewport size between 800px and 1024px. This explains why in our example, the desktop view is showing despite the actual device width.</p> <h2 id="viewport-control-with-the-meta-tag" tabindex="-1">Viewport control with the meta tag <a class="header-anchor" href="https://bitsofco.de/responsive-design-viewport/">#</a></h2> <p>One of the metadata we can pass with the meta tag is <code>viewport</code>. This allows us to control certain aspects of the viewport and solve the discrepancy. The tag follows the following structure -</p> <pre><code>&lt;meta name=&quot;viewport&quot; content=&quot;key=value, key=value&quot;&gt; </code></pre> <p>There are 6 &quot;keys&quot; we can pass for the content -</p> <ul> <li>width</li> <li>height</li> <li>initial-scale</li> <li>minimum-scale</li> <li>maximum-scale</li> <li>user-scalable</li> </ul> <h3 id="width" tabindex="-1">Width <a class="header-anchor" href="https://bitsofco.de/responsive-design-viewport/">#</a></h3> <p>This allows us to set the width of the viewport ourselves, instead of relying on the inaccurate default. We can set the width to a specific number in pixels, but it is advisable to set the width equal to the device width. This allows us to accommodate varying screen sizes.</p> <pre><code>&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt; </code></pre> <p>By adding this, we now get the accurate view.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/c0TJkYYVMh-250.avif 250w"><source type="image/webp" srcset="https://bitsofco.de/img/c0TJkYYVMh-250.webp 250w"><img alt="Viewport Resizing" loading="lazy" decoding="async" src="https://bitsofco.de/img/c0TJkYYVMh-250.png" width="250" height="445"></picture> (iPhone 6, With width=device-width)</p> <h3 id="initial-scale" tabindex="-1">Initial-scale <a class="header-anchor" href="https://bitsofco.de/responsive-design-viewport/">#</a></h3> <p>On mobile devices, users have the ability to zoom in and out on the page. When a user zooms in, this changes the viewport pixel sizing, but not the CSS pixel sizing.</p> <blockquote> <p>For example, we have an element of <code>width: 100px;</code> within a viewport of 400px width. At this point, the element spans 1/4 of the screen. If the user zooms in to a scale of 2, the viewport width now becomes 200px, but the element remains 100 CSS pixels wide. At this point, the element spans 1/2 of the screen.</p> </blockquote> <p>Setting the initial-scale tells the browser what the ratio between CSS pixels and viewport pixels should be on the initial load of the page. Generally, this will be 1:1.</p> <p>This parameter is paticularly needed to address a problem caused by setting <code>width=device-width</code>&gt;. Some mobile browsers by default will use the width of the device in portrait mode, and so an inaccurate view will be given when the devide is rotated to landscape orientation.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/uxuOkrZpsg-400.avif 400w"><source type="image/webp" srcset="https://bitsofco.de/img/uxuOkrZpsg-400.webp 400w"><img alt="Viewport Resizing" loading="lazy" decoding="async" src="https://bitsofco.de/img/uxuOkrZpsg-400.png" width="400" height="225"></picture> (iPhone 6 in Landscape, Without initial scale)</p> <p>As you can see, when the iPhone is in landscape, we are still seeing the view for widths of less than 500px. This can be resolved by setting the initial-scale to 1.</p> <pre><code>&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt; </code></pre> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/qLySILxbGl-400.avif 400w"><source type="image/webp" srcset="https://bitsofco.de/img/qLySILxbGl-400.webp 400w"><img alt="Viewport Resizing" loading="lazy" decoding="async" src="https://bitsofco.de/img/qLySILxbGl-400.png" width="400" height="225"></picture> (iPhone 6 in Landscape, With initial scale)</p> <h3 id="user-scalable-maximum-scale-and-minimum-scale" tabindex="-1">User-scalable, maximum-scale, and minimum-scale <a class="header-anchor" href="https://bitsofco.de/responsive-design-viewport/">#</a></h3> <p>These parameters control the user's ability to pinch-to-zoom on the page.</p> <ul> <li> <p><strong>Maximum-scale</strong> - The maximum scale that a user can zoom in on the page. If set to 1, for example, the user will not be able to zoom in at all. If set to 2, the user will only be able to zoom in to a ratio of 1 CSS pixel to 2 viewport pixels.</p> </li> <li> <p><strong>Minimum-scale</strong> - The minimum scale that a user can zoom in on the page. For example, if this is set to 2, the user will be zoomed in by a 1:2 ratio by default and will not be able to zoom out to 1:1.</p> </li> <li> <p><strong>User-scalable</strong> - If set to <code>user-scalable=no</code>, the user will not be able to zoom in or out at all.</p> </li> </ul> <p>Because these parameters restrict the users ability to access content, it is generally advised that they not be used.</p> <blockquote> <p>every time you prevent zooming on your website a kitten dies <a href="https://t.co/eokrHZ6ieL">https://t.co/eokrHZ6ieL</a> — Brent Jackson (@jxnblk) <a href="https://twitter.com/jxnblk/status/580519668655493121">March 25, 2015</a></p> </blockquote> <h2 id="viewport-control-with-css" tabindex="-1">Viewport control with css <a class="header-anchor" href="https://bitsofco.de/responsive-design-viewport/">#</a></h2> <p>Although it is very widely supported, the viewport meta tag is not a formal part of HTML5. Instead, the <a href="https://www.w3.org/TR/css-device-adapt/">W3C is working on implementing it into CSS</a> with the<code>@viewport</code> rule. The <code>@viewport</code> rule works in a similar way to the meta tag and allows for the same declarations.</p> <pre><code>@viewport { zoom: 1.0; /* same as initial-scale=1 */ width: device-width; } /* Vendor specific prefixes */ @-ms-viewport @-webkit-viewport @-moz-viewport @-o-viewport </code></pre> <p>Until the <code>@viewport</code> rule is formalised, you should stick with the meta tag or use both.</p> <h2 id="tl-dr" tabindex="-1">TL;DR <a class="header-anchor" href="https://bitsofco.de/responsive-design-viewport/">#</a></h2> <p>To ensure your media queries work on hand-held devices, put this in your <code>&lt;head&gt;</code>!</p> <pre><code>&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt; </code></pre> <p>You can test the examples out for yourself on my <a href="https://github.com/ireade/viewports">github repo</a>.</p> Tic Tac Toe 2015-03-24T00:00:00Z https://bitsofco.de/tic-tac-toe/ <p>As valuable as it is to understand the theory behind the code we write, nothing is more important than just writing code! So, as part of the blog, I am aiming to do at least one &quot;project&quot; every month. This will range from small, refactoring projects to bigger, more ambitious builds.</p> <h2 id="project-overview" tabindex="-1">Project overview <a class="header-anchor" href="https://bitsofco.de/tic-tac-toe/">#</a></h2> <p>For my first project, I decided to build a game of tic tac toe using JavaScript &amp; jQuery. I have seen a few of these around, but most of them are two human-player games. What I wanted to build was a single player game, with the program as the opposition. This meant that the program had to be smart enough, not only to know when there was a win/lose/draw, but also to play tactically.</p> <h2 id="my-solution" tabindex="-1">My solution <a class="header-anchor" href="https://bitsofco.de/tic-tac-toe/">#</a></h2> <p>You can view the full code on <a href="https://github.com/ireade/tictactoe">my github repository</a>, as well as <a href="https://ireade.github.io/tictactoe/">play the game yourself</a>. I thought I would share with you here the pseudocode to show the basic logic of the program.</p> <p>The solution I came up with used classes on each of the tiles to determine when there was a win/lose/draw state. First, I created the actual playing board, which was just a bunch of <code>&lt;div&gt;</code>s.</p> <pre><code>&lt;div class=&quot;tic-tac-toe&quot;&gt; &lt;div id=&quot;square1&quot; class=&quot;tile free&quot;&gt;&lt;/div&gt; &lt;div id=&quot;square2&quot; class=&quot;tile free&quot;&gt;&lt;/div&gt; &lt;div id=&quot;square3&quot; class=&quot;tile free&quot;&gt;&lt;/div&gt; &lt;div id=&quot;square4&quot; class=&quot;tile free&quot;&gt;&lt;/div&gt; &lt;div id=&quot;square5&quot; class=&quot;tile free&quot;&gt;&lt;/div&gt; &lt;div id=&quot;square6&quot; class=&quot;tile free&quot;&gt;&lt;/div&gt; &lt;div id=&quot;square7&quot; class=&quot;tile free&quot;&gt;&lt;/div&gt; &lt;div id=&quot;square8&quot; class=&quot;tile free&quot;&gt;&lt;/div&gt; &lt;div id=&quot;square9&quot; class=&quot;tile free&quot;&gt;&lt;/div&gt; &lt;/div&gt; </code></pre> <p>Next, I mapped out what every winning state could be.</p> <p><img src="https://i.stack.imgur.com/p8t8I.png" alt="Tic Tac Toe Numbered"></p> <p>In total there were 8 winning states, e.g. 1-2-3 or 1-5-9. When the user clicked on a tile, it added the class 'X-play', and when the program played, it added the class 'O-play'. With this, I could write a statement like:</p> <pre><code>if ( sq1.hasClass('X-play') &amp;&amp; sq2.hasClass('X-play') &amp;&amp; sq3.hasClass('X-play') ) { // X wins } </code></pre> <p>This method involved a lot of hard-coding to enable the program to determine the winning states and also how to play tactically. I had 9 functions in total -</p> <pre><code>function validatePlay { // check if the square attempted has already been played // if square is available // return true // else // return false } function clearBoard { // resets the board to blank state } function winAlert { // alerts who won the game // calls clearBoard } function checkWin { // checks if any of the winning states were played by the same player // if true // call winAlert } function checkDraw { // checks if all the tiles were played, but there is no win // if true // alert that there has been a draw // call clearBoard } function Oplay { function Oplaying { // call validatePlay on square passed // if play is valid // play square // else // call Orandomplay } function Orandomplay { // loop through numbers 1 to 9 to find a square to play // call validatePlay // if play is valid // play square and break loop // else // continue loop } // Check for tactical play option // Check which squares have been played and which O should play in response // e.g. if square 1 and square 2 have been played by X, O should play square 3 // If tactical option available // call Oplaying(tacticalsquare) // If no tactical option, // call Orandomplay // call checkDraw // call checkWin } function Xplay { // check which square was played by user // call validatePlay on square played // if play is valid // play square // call checkDraw // call checkWin // call Oplay } </code></pre> <p>That's the basic idea! Although this might not be the most elegant solution, it works well enough for the moment. There are still some bugs and other things that I will like to improve on in the future. One thing I would definitely like to do is to re-write the program without jQuery in order to get more comfortable writing pure JavaScript.</p> <p>If you have any suggestions, please do leave a comment below.</p> <ul> <li><a href="https://github.com/ireade/tictactoe">My github repository</a></li> <li><a href="https://ireade.github.io/tictactoe/">Play the game!</a></li> </ul> <p><strong>Update:</strong> I rebuilt this game again, 8 months later. <a href="https://bitsofco.de/tic-tac-toe-revisited">Read the new article</a>.</p> Meta Charset 2015-03-17T00:00:00Z https://bitsofco.de/meta-charset/ <pre><code>&lt;meta charset=&quot;UTF-8&quot;&gt; </code></pre> <p>As a self-taught front-end developer, this is a line of code that you may have been mechanically writing without completely understanding. For me, it was something that I offhandedly wrote, even occasionally missing altogether, until I did some research and discovered its importance.</p> <p>Simply put, when you declare the &quot;charset&quot; as &quot;UTF-8&quot;, you are telling your browser to use the UTF-8 character encoding, which is a method of converting your typed characters into machine-readable code.</p> <h2 id="character-set-vs-character-encoding" tabindex="-1">Character set vs character encoding <a class="header-anchor" href="https://bitsofco.de/meta-charset/">#</a></h2> <p>Your computer can only manipulate information it receives in binary format. This means that any characters on your web page must be converted. This is done in 2 stages - character set and character encoding.</p> <ol> <li>Each letter, punctuation mark, or character is assigned a unique number, called a &quot;code unit&quot; (Character Set)</li> <li>The code unit is then converted to binary (Character Encoding)</li> </ol> <p>HTML documents can only contain characters defined by the Unicode character set, so we do not need to define the <em>character set</em> in our document. However, there are several forms of <em>encoding</em> that can be used with Unicode, so we do need to declare which we would like to use. Presently, UTF-8 is the recommended character encoding by the W3C.</p> <p>As an example, the text &quot;Hello, World!&quot;, is converted to binary in the following way.</p> <p>Hello, World!</p> <p>Character Set (Unicode)</p> <p>U+0048 U+0065 U+006C U+006C U+006F U+002C U+0020 U+0057 U+006F U+0072 U+006C U+0064 U+0021</p> <p>Character Encoding (UTF-8)</p> <p>01001000 01100101 01101100 01101100 01101111 00101100 00100000 01010111 01101111 01110010 01101100 01100100 00100001</p> <h2 id="character-references-and-entities" tabindex="-1">Character references and entities <a class="header-anchor" href="https://bitsofco.de/meta-charset/">#</a></h2> <p>Within HTML, we occasionally need to access characters from Unicode besides what is on a standard keyboard. To solve this problem, we can use &quot;numeric character references&quot; and &quot;named character entities&quot; to reference them.</p> <p>Each of these Unicode characters has both a named entity as well as a numeric reference, and you can call either one. For example, one that you may frequently use is the copyright symbol, which can be written as -</p> <ul> <li><code>&amp;copy;</code>(Named entity)</li> <li><code>&amp;169;</code> (Numeric reference)</li> </ul> <h2 id="why-you-need-to-declare-character-encoding" tabindex="-1">Why you need to declare character encoding <a class="header-anchor" href="https://bitsofco.de/meta-charset/">#</a></h2> <p>You may notice in certain situations that, if you skip on declaring the character encoding, nothing significant happens. This is because the character encoding can be specified elsewhere. There are actually three main methods, listed below in order of precedence they take (highest to lowest).</p> <ol> <li><a href="https://www.w3.org/International/questions/qa-byte-order-mark.en.php">Byte order mark (BOM)</a></li> <li><a href="https://www.w3.org/International/O-HTTP-charset">HTTP Content-Type</a></li> <li>Meta tag</li> </ol> <p>The first two methods are not so easily accessible and may be present without your knowledge. You can use the <a href="https://validator.w3.org/i18n-checker/">W3C Internationalization Checker</a> to look these up for your website.</p> <p>Even though the Meta tag may be overridden by the other two methods, it is still advised that you specify it for a few reasons.</p> <ul> <li>Clarity - it helps other people reading your code more easily determine what character encoding you intended to be used.</li> <li>Wrongful encoding - in some cases, particularly for static websites, the character encoding may not be specified by these other methods. In this case, you risk incorrect encoding of your content.</li> <li>Validation - <a href="https://validator.w3.org/docs/help.html#faq-charset">according to the W3C</a>, declaring you character encoding through the meta charset tag is essential for your code to validate</li> </ul> <h2 id="how-to-declare-character-encoding" tabindex="-1">How to declare character encoding <a class="header-anchor" href="https://bitsofco.de/meta-charset/">#</a></h2> <p>The proper way to declare your character encoding is to state it immediately after the opening head in your document, before anything else.</p> <pre><code>&lt;!doctype html&gt; &lt;html&gt; &lt;head&gt; &lt;meta charset=&quot;UTF-8&quot;&gt; &lt;title&gt;My Website&lt;/title&gt; &lt;/head&gt; &lt;body&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> The Value of Blogging 2015-03-16T00:00:00Z https://bitsofco.de/value-of-blogging/ <p>Since I began my career as a self-employed web designer/developer, my main struggle has been with demonstrating to clients my technical ability and level of talent. I have a <a href="https://www.ireaderinokun.com">portfolio website</a> that showcases my latest work, and it does elicit a positive response from prospective clients. However, the portfolio alone doesn't convey to them why I should be paid more than any other designer, or even why they should pick me over a cheaper templating solution.</p> <p>So I did some research, looking at the personal websites of some of the most successful freelance designers and developers. I tried to determine what the common thread was that made them so successful, and the answer was actually quite simple.</p> <blockquote> <p>From an outsiders perspective, all these freelancers appear to be experts in their respective fields.</p> </blockquote> <p>When you visit their site, you get the impression that they are who other designers/developers go to for their expertise.</p> <p>These freelancers don't give this impression by simply showcasing websites they have built. In fact, for most of them, their actual work is not the focus of their website. So how do they give this impression? <em>They teach others</em>. Whether this be by giving talks at developer conferences, hosting a podcast, or simply writing a blog. They become an 'expert' by constantly growing their knowledge and sharing it with others.</p> <h2 id="am-i-an-expert" tabindex="-1">Am I an expert? <a class="header-anchor" href="https://bitsofco.de/value-of-blogging/">#</a></h2> <p>As I have <a href="https://bitsofco.de/hello-world">mentioned before</a>, I tend to be a hard on myself about my skill set. For the longest time, I would imagine those successful developers as having knowledge that was completely unattainable by me. I imagined that only they could share their knowledge through blogs/podcasts/talks because their knowledge was complete. It was only through the advice of various people that this fog was gradually lifted.</p> <blockquote> <p>An expert isn't someone with perfect, unimpaired knowledge. No such person exists.</p> </blockquote> <p>It took me a while to realise that people in all stages of their learning could still share their knowledge and teach others. Whenever I look back at work I have done, even 3 months ago, I'm always surprised at how much I can improve due to what I have learned. This means that the knowledge I have right now could still be valuable to people at the stage I was 3 months ago. Although there is a lot that I am still learning, I have gained a lot of 'expertise' over the years, which I could and should share.</p> <h2 id="learning-how-to-blog" tabindex="-1">Learning how to blog <a class="header-anchor" href="https://bitsofco.de/value-of-blogging/">#</a></h2> <p>This brings me to the reason for this post. I have been participating in an email course on blogging for your career by the <a href="https://simpleprogrammer.com">Simple Programmer, John Sonmez</a>. The <a href="https://devcareerboost.com/blog-course/">free 3 week course</a> takes you through the steps of creating a successful blog, from coming up with your theme and registering your domain name to generating consistent traffic.</p> <p>John has clearly put a lot of work into the course, and it has been so valuable to me starting up this blog. If you are a self-employed web designer like myself, I cannot recommend this course enough!</p> <p>The most valuable takeaway from the course I have received is to <em>just start blogging</em>, and consistently. The more you do it, the better you will get at it, and more you will reap from it.</p> <p>My usual Bits of Code post will come out as scheduled tomorrow.</p> Doctype 2015-03-10T00:00:00Z https://bitsofco.de/doctype/ <p>For my first post, I thought I would start off with the <code>&lt;!doctype&gt;</code> tag, the first thing you write on any HTML document.</p> <p>The <code>&lt;!doctype&gt;</code> (Document Type Declaration) tag is what indicates to your browser and other HTML-parsing tools what version of HTML you are writing and therefore how to interpret it. Before HTML5, there were different co-existing versions of HTML you could use to markup your website. These versions all had slightly different ways of writing HTML, and accepted slightly different tags. So, you had to tell your browser which version of HTML you were writing, in order for your markup to be effective. For example, if you wanted to centre an element within your document, you could use a <code>&lt;center&gt;</code> tag if you were writing HTML4.01 Transitional, but not if you were writing HTML4.01 Strict.</p> <p>In my early days - i.e. when I used to just copy and paste code - I never considered what this tag really was and the effects it would have on what I wrote, and ran into a lot of problems with these cross-version differences.</p> <p>W3Schools has an extensive list of the <a href="https://www.w3schools.com/tags/ref_html_dtd.asp">HTML elements and doctypes that support them</a> if you are interested.</p> <h2 id="why-you-still-need-to-declare-your-doctype" tabindex="-1">Why you still need to declare your doctype <a class="header-anchor" href="https://bitsofco.de/doctype/">#</a></h2> <p>Because HTML5 is now the standard, if you skip on your doctype, most modern browsers will still be able to determine what you are writing. However, you should still declare your doctype for two main reasons.</p> <h3 id="cross-browser-support-i-e-ie" tabindex="-1">Cross Browser Support (i.e., IE) <a class="header-anchor" href="https://bitsofco.de/doctype/">#</a></h3> <p>Different browsers handle a missing or wrong doctype differently, and this can mess up the DOM. To test this out, I used the HTML5 <code>&lt;audio&gt;</code> tag, but did not declare a doctype. In my css, I wrote a line targeting the <code>&lt;audio&gt;</code> tag in addition to other styling.</p> <pre><code>audio { border: 10px solid red; } </code></pre> <p>These were the results across different browsers and versions.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/KYcQXOJ1On-1000.avif 1000w"><source type="image/webp" srcset="https://bitsofco.de/img/KYcQXOJ1On-1000.webp 1000w"><img alt="Audio Tag on Firefox" loading="lazy" decoding="async" src="https://bitsofco.de/img/KYcQXOJ1On-1000.jpeg" width="1000" height="565"></picture> <em>Firefox 30 on Windows 8.1</em> - Works fine!</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/82Z8GUGcMc-1000.avif 1000w"><source type="image/webp" srcset="https://bitsofco.de/img/82Z8GUGcMc-1000.webp 1000w"><img alt="Audio Tag on IE" loading="lazy" decoding="async" src="https://bitsofco.de/img/82Z8GUGcMc-1000.jpeg" width="1000" height="565"></picture> <em>IE 9 on Windows 7</em> - The page was broken. None of my css rendered, including the css that wasn’t related to the <code>&lt;audio&gt;</code> tag.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/KTdjE2ZL4--1000.avif 1000w"><source type="image/webp" srcset="https://bitsofco.de/img/KTdjE2ZL4--1000.webp 1000w"><img alt="Audio Tag on Opera" loading="lazy" decoding="async" src="https://bitsofco.de/img/KTdjE2ZL4--1000.jpeg" width="1000" height="565"></picture> <em>Opera 12.15, IE 6 on MacOSX</em> - The entire page broke and nothing displayed at all.</p> <h3 id="validation-aka-writing-good-code" tabindex="-1">Validation (aka, writing good code) <a class="header-anchor" href="https://bitsofco.de/doctype/">#</a></h3> <p>As a developer, you want to write code that is as semantically valid as possible, and a doctype declaration is mandatory for your code to validate. There is a more extensive article by W3 on <a href="https://validator.w3.org/docs/why.html">why you need to be writing valid code</a>.</p> <h2 id="how-to-declare-the-doctype" tabindex="-1">How to declare the doctype <a class="header-anchor" href="https://bitsofco.de/doctype/">#</a></h2> <p>With HTML5 came the simplified document type declaration. Instead of declaring what specific iteration of HTML you want to follow, you only need to write:</p> <pre><code>&lt;!doctype html&gt; </code></pre> <p>This upholds your page to the standards of the latest version of HTML. You can find the methods to <a href="https://www.w3schools.com/tags/tag_DOCTYPE.asp">declare the other versions of HTML here</a> if you are interested.</p> <p>Please leave a comment below if you have any feedback, questions, or concerns.</p> Hello, World! 2015-03-03T00:00:00Z https://bitsofco.de/hello-world/ <p>My name is Ire (pronounced e-ray), and I am a freelance web designer/developer from Lagos, Nigeria. Growing up, I was always really interested in technology and the internet. I made my first website in 2005 when I was 14 (a fan page for Neopets, if you were wondering).</p> <p>Over the years, I have dabbled in web design and development as a hobby, making some poorly designed websites and writing some pretty awful code. Because I never got a fully formal developer education, there were a few things that I didn’t know, and I didn’t know that I didn’t know them. As an example, I only discovered CSS pre-processors about a year ago, and it has literally transformed my workflow since!</p> <p>Nowadays, I have improved a lot in my skill. I’m plugged in to as many designer and hacker news outlets as I can to make sure that I’m doing things right. Even so, there are still things that I don’t know. Whether that is due to the ever changing nature of the industry, or the fact that I work alone, I am constantly learning new things to improve my craft. And I can’t be the only one.</p> <p>So I have decided to start this blog, “bits of code”, where I share my ever growing knowledge about web development. Particularly little code bits that, as a developer, you could get away with not writing, but you should. Or things that you may be writing but don’t really understand why. I’m sure there will be things I just learn that pretty much all developers already know about, but I guess I have accepted that I might look stupid!</p> <p>As I am currently a designer and front-end developer, most of my posts will be related to those areas. However, I am aiming to be full-stack and am learning Ruby as well. I hope to do one of those Rails challenges at some point during the year.</p> <h2 id="a-credit" tabindex="-1">A Credit <a class="header-anchor" href="https://bitsofco.de/hello-world/">#</a></h2> <p>I was inspired to start this blog by <a href="https://twitter.com/jcutrell">Jonathan Cutrell</a> and <a href="https://twitter.com/jsonmez">John Sonmez</a> in <a href="https://developertea.com/episodes/7976">Episode 21 of Developer Tea</a>. If you are a developer and haven’t already, go listen to this now!</p> <h2 id="my-first-website" tabindex="-1">My First Website <a class="header-anchor" href="https://bitsofco.de/hello-world/">#</a></h2> <p>In the interest of looking stupid, here is one of my first websites I created back in 2005. I only have the test version I created, but you get the general idea.</p> <p><picture><source type="image/avif" srcset="https://bitsofco.de/img/hhQTvRafSg-1631.avif 1631w"><source type="image/webp" srcset="https://bitsofco.de/img/hhQTvRafSg-1631.webp 1631w"><img alt="My First Website Layout" loading="lazy" decoding="async" src="https://bitsofco.de/img/hhQTvRafSg-1631.png" width="1631" height="864"></picture></p> <p>The design is actually not awful! I guess you can't go too wrong with a minimal site. But the code..</p> <pre><code>&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot;&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;&amp;spooke designs.&lt;/title&gt; &lt;style type=&quot;text/css&quot;&gt; .banner {position:absolute; overflow: none; left: 50px; top:35px;} .content {position:absolute; overflow: auto; left: 210px; top:193px; width:490px; height:800px} .nav {position: absolute; overflow: none; left: 56px; top:193px; width:137px; height:800px} font, body, td{font-family: small fonts; font-size: 7pt; color: #000000;} a:link, a:visited, a:active {font-family: small fonts; font-size: 7pt; color: #000000;} a:hover {font-family: small fonts; font-size: 7pt; color: #000000;} body {background: url(&quot;file:///C:/Documents%20and%20Settings/Administrator.IREOLUWA/Desktop/spooke/background.bmp&quot;); font-size: 7pt; font-family:&quot;small fonts&quot;; color: #D4A274;} &lt;/style&gt; &lt;/head&gt; &lt;body&gt; &lt;div class=&quot;banner&quot;&gt; &lt;img style=&quot;width: 766px; height: 148px;&quot; alt=&quot;&quot; src=&quot;banner2.PNG&quot;&gt;&lt;/div&gt; &lt;div class=&quot;nav&quot;&gt; &lt;b&gt;navigation.&gt;&gt;&lt;/b&gt;&lt;br&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;br&gt; &amp;.&lt;a href=&quot;&quot;&gt;LINK HERE&lt;/a&gt; &lt;/div&gt; &lt;div class=&quot;content&quot;&gt; &lt;center&gt;MINI-FEED BOX HERE &lt;p&gt;&lt;/p&gt; &lt;p&gt;iframe here&lt;br&gt; text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text &lt;br&gt; &lt;br&gt; CREDITS &lt;/p&gt; &lt;/center&gt; &lt;/div&gt; &lt;!-- --&gt; &lt;script type=&quot;text/javascript&quot; src=&quot;/i.js&quot;&gt;&lt;/script&gt;&lt;!-- --&gt; &lt;script type=&quot;text/javascript&quot; src=&quot;/i.js&quot;&gt;&lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </code></pre> <p>So many things...</p> <p>I hope you enjoyed my first post. Please leave a comment down below if you have any questions or comments.</p>