Filament Group, Inc: Lab2018-12-26T00:00:00-05:00Copyright (c) 2012, Filament GroupFilament Group, IncBuild your own Blog from Scratch using Eleventy2018-12-26T00:00:00-05:00<p style="float: left; margin: 0 5% 2% 0; max-width: 30%"><a href="https://11ty.io/"><img src="/images/eleventy/logo.png" alt="Eleventy Logo" style="display: block;"></a></p>
<p>Does the command line stress you out? Have you been burned one too many times by confusing error messages and unintuitive syntax? Don’t worry, that’s pretty common. If you invest a little time learning about the command line, you can educate yourself out of those feelings and unlock a new tier of productivity.</p>
<p>Eleventy is a beginner-friendly static site generator that runs on the command prompt and using it will make you more productive. <em>(A static site generator is a program that combines templates and data to create HTML.)</em> Today we’re going to use Eleventy to build a blog web site that lets us write our content in Markdown.</p>
<h2 id="project-highlights">Project Highlights <a class="direct-link" href="#project-highlights" aria-hidden="true">#</a></h2>
<p>Before we dive in, let me tell you a little bit about Eleventy’s project goals.</p>
<p>Eleventy is flexible. It wants to work with your project’s directory structure.</p>
<p>Eleventy is fast. Eleventy includes a <a href="https://github.com/11ty/eleventy-benchmark">benchmark with 10,000 templates</a> (12KB each, 120MB total) that runs in about 17 seconds. That’s 1.7ms per template.</p>
<p>Eleventy doesn’t lock you into one specific template language. Out of the box, Eleventy works with HTML, Markdown, Liquid, Nunjucks, Handlebars, Mustache, EJS, HAML, and JavaScript files. You can use one or more of these. You can mix them together.</p>
<p>Eleventy is gently iterative. You can migrate your existing content slowly over to use Eleventy. You can use Eleventy to migrate your content to a new template language, one template at a time.</p>
<p>It’s being used by <a href="https://www.11ty.io/docs/sites/">lots of cool sites</a>, including the very site you’re reading right now (<a href="https://www.filamentgroup.com/">filamentgroup.com</a>) and the site for <a href="https://v8.dev/">Google’s V8 project</a>.</p>
<h2 id="installation">Installation <a class="direct-link" href="#installation" aria-hidden="true">#</a></h2>
<p>But before we get too far along we need to have Node installed. Node comes as an installable program you can <a href="https://nodejs.org/en/">download from the web</a>. Bask in the safety of this GUI installer and comfortably click through each wizard step with confidence.</p>
<p>Before we lull ourselves into a false sense of security, let’s hop on this bull and ride. We need to open our terminal, command line, or command prompt window. On a Mac this means finding the <code>Terminal.app</code> (usually in <code>/Applications/Utilities/Terminal.app</code>) and opening it. On Windows this is called <code>cmd.exe</code> (found in the Start Menu under <code>Start &gt; Program Files &gt; Accessories &gt; Command Prompt</code>).</p>
<p>Make sure Node is installed correctly by running <code>node --version</code>. Eleventy requires Node 8. You’ll know if you have Node 8 or higher if the first number after the <code>v</code> is greater than or equal to 8. For example, if <code>node --version</code> returns <code>v8.11.3</code>, you have Node 8.</p>
<p>To install Eleventy, you’ll need to enter the following command in your command prompt and press enter to execute it:</p>
<pre class="language-bash"><code class="language-bash"><span class="highlight-line"><span class="token function">npm</span> <span class="token function">install</span> -g @11ty/eleventy</span></code></pre>
<p><em>Read more about <a href="https://www.11ty.io/docs/local-installation/">installing locally in a project instead of globally</a></em></p>
<h2 id="running-eleventy-for-the-first-time">Running Eleventy for the First Time <a class="direct-link" href="#running-eleventy-for-the-first-time" aria-hidden="true">#</a></h2>
<p>The simplest thing that Eleventy can help you do is transform a markdown file into HTML. Make a new directory for our project called <code>eleventy-intro</code>…</p>
<pre><code>mkdir eleventy-intro
cd eleventy-intro
</code></pre>
<p>…and create a new file called <code>blog-post.md</code>. Save the following contents in <code>blog-post.md</code>:</p>
<pre class="language-markdown"><code class="language-markdown"><span class="highlight-line"><span class="token title important"><span class="token punctuation">#</span> This is my Title</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">This is a paragraph of text.</span></code></pre>
<p>Now you can run Eleventy:</p>
<pre class="language-bash"><code class="language-bash"><span class="highlight-line">eleventy</span></code></pre>
<pre><code>Writing _site/blog-post/index.html from ./blog-post.md.
Processed 1 file in 0.13 seconds
</code></pre>
<h2 id="make-it-valid-html">Make it Valid HTML <a class="direct-link" href="#make-it-valid-html" aria-hidden="true">#</a></h2>
<p>We’ve successfully transformed a markdown file into HTML. Congratulations! But if we open that file <code>_site/blog-post/index.html</code> to see our HTML output—it’s not valid HTML.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>This is my Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"><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 paragraph of text.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></span></code></pre>
<p>To make it valid without adding a bunch of junk inside of our markdown file, let’s add an <a href="https://www.11ty.io/docs/layouts/">Eleventy layout</a>. This will wrap a template with other stuff.</p>
<p>Save these contents into <code>_includes/layout.liquid</code>:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token doctype">&lt;!doctype html></span></span><br><span class="highlight-line"><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">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <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">=</span><span class="token punctuation">"</span>utf-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">></span></span>My Blog<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>{{ pageTitle }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"></span><br><span class="highlight-line"> {{ content }}</span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></span></code></pre>
<p>If this is the first time you’ve seen a <code>liquid</code> template and are not familiar with the syntax, you may want to take a second and head over to the <a href="https://shopify.github.io/liquid/">Shopify Liquid documentation</a> to learn more. There is one main idea on display here: Double curly braces are used to output a variable (e.g. <code>{{ pageTitle }}</code>).</p>
<p>In our layout example we use <code>{{ content }}</code> to denote where our markdown template content will go.</p>
<p>Now we need to tell our <code>blog-post.md</code> file to use the layout. Let’s add something to the top of our file to do just that:</p>
<pre class="language-markdown"><code class="language-markdown"><ins class="highlight-line highlight-line-add"><span class="token hr punctuation">---</span></ins><br><ins class="highlight-line highlight-line-add"><span class="token title important">layout: layout.liquid</ins><br><ins class="highlight-line highlight-line-add"><span class="token punctuation">---</span></span></ins><br><span class="highlight-line"><span class="token title important"><span class="token punctuation">#</span> This is my Title</span></span><br><span class="highlight-line"></span><br><span class="highlight-line">This is a paragraph of text.</span></code></pre>
<p>This section between (and including) the two <code>---</code> marks is called <a href="https://www.11ty.io/docs/data-frontmatter/">YAML Front Matter</a>. It’s a way to write data into our template file that can be used by Liquid templates. If you’ve used Jekyll this may look familiar.</p>
<p>The <code>layout</code> key tells Eleventy to look for files inside of the <code>_includes</code> folder to wrap around the content of the template. Specifically, we want our blog post to use the <code>_includes/layout.liquid</code> file as a wrapper layout.</p>
<p>Now, let’s move our title text into our <code>pageTitle</code> front matter so that it can be used in our layout file. Now our <code>blog-post.md</code> looks like this:</p>
<pre class="language-markdown"><code class="language-markdown"><span class="highlight-line"><span class="token hr punctuation">---</span></span><br><span class="highlight-line">layout: layout.liquid</span><br><ins class="highlight-line highlight-line-add"><span class="token title important">pageTitle: This is my Title</ins><br><span class="highlight-line"><span class="token punctuation">---</span></span></span><br><span class="highlight-line">This is a paragraph of text.</span></code></pre>
<p>Let’s run Eleventy again to generate our output:</p>
<pre class="language-bash"><code class="language-bash"><span class="highlight-line">eleventy</span></code></pre>
<p>Open the output file at <code>_site/blog-post/index.html</code> to see what happened:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token doctype">&lt;!doctype html></span></span><br><span class="highlight-line"><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">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <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">=</span><span class="token punctuation">"</span>utf-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">></span></span>My Blog<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>This is my Title<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <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 paragraph of text.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></span></code></pre>
<h2 id="apply-changes-automatically">Apply Changes Automatically <a class="direct-link" href="#apply-changes-automatically" aria-hidden="true">#</a></h2>
<p>It will be tiring to re-run Eleventy every time we make a change. Let’s use <code>--serve</code> to run a hot-reloading local web server that will apply our changes automatically when you save.</p>
<pre class="language-bash"><code class="language-bash"><span class="highlight-line">eleventy --serve</span></code></pre>
<p>Your command prompt will tell you what URL to navigate to but the default is: <a href="http://localhost:8080/blog-post/"><code>http://localhost:8080/blog-post/</code></a></p>
<p>Try editing our <code>blog-post.md</code> file and saving it. Eleventy will run automatically <em>and</em> the browser window will refresh automatically when it’s done.</p>
<h2 id="let%E2%80%99s-make-a-blog!">Let’s make a Blog! <a class="direct-link" href="#let%E2%80%99s-make-a-blog!" aria-hidden="true">#</a></h2>
<p>Why not? We’re actually pretty close to making a simple blog! Let’s take it a little further, shall we? Let’s make a <code>posts</code> folder for our blog posts and move our <code>blog-post.md</code> file in there. Then add a <code>posts/posts.json</code> file with the following content:</p>
<pre class="language-json"><code class="language-json"><span class="highlight-line"><span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token property">"layout"</span><span class="token operator">:</span> <span class="token string">"layout.liquid"</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Instead of requiring us to edit everything in our <code>posts</code> folder using YAML Front Matter, this data file will apply the data inside to all of the files in our <code>posts</code> folder. This will work for any <code>.json</code> file name that matches its parent folder name (e.g. <code>posts/posts.json</code> or <code>pictures/pictures.json</code>). Read more about <a href="https://www.11ty.io/docs/data-template-dir/">Template and Directory Data Files</a>. If you use both data files and front matter, any duplicate keys in the front matter will take precedence over the data file entries.</p>
<p>Now try making another blog post in <code>posts/blog-post-2.md</code>:</p>
<pre class="language-markdown"><code class="language-markdown"><span class="highlight-line"><span class="token hr punctuation">---</span></span><br><span class="highlight-line"><span class="token title important">pageTitle: This is my other Title</span><br><span class="highlight-line"><span class="token punctuation">---</span></span></span><br><span class="highlight-line">This is another paragraph of text.</span></code></pre>
<p>Our <code>layout</code> is being applied from <code>posts/posts.json</code> so we don’t need that in our front matter any more. We do want a <code>pageTitle</code> though!</p>
<p>Is our server still applying changes automatically? If so, check out our new blog post at <a href="http://localhost:8080/posts/blog-post-2/"><code>http://localhost:8080/posts/blog-post-2/</code></a>. Our old post should now live at <a href="http://localhost:8080/posts/blog-post/"><code>http://localhost:8080/posts/blog-post/</code></a>.</p>
<h3 id="finish-with-a-home-page">Finish with a Home Page <a class="direct-link" href="#finish-with-a-home-page" aria-hidden="true">#</a></h3>
<p>Doing great so far. We have two blog posts. But if we go to the root of our site (<code>http://localhost:8080/</code>) there’s nothing there! Let’s add a home page that lists all of our lovely blog posts.</p>
<p>Open up your <code>posts/posts.json</code> and add a <code>tags</code> array to our posts data:</p>
<pre class="language-json"><code class="language-json"><span class="highlight-line"><span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token property">"layout"</span><span class="token operator">:</span> <span class="token string">"layout.liquid"</span><span class="token punctuation">,</span></span><br><ins class="highlight-line highlight-line-add"> <span class="token property">"tags"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"posts"</span><span class="token punctuation">]</span></ins><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Any time you use <code>tags</code>, Eleventy will make a new collection of all the pieces of content that use the same tag. Now our blog posts will be available as a collection, specifically under the data <code>collections.posts</code>. Read more about <a href="https://www.11ty.io/docs/collections/">Eleventy Collections</a>.</p>
<p>Let’s make an <code>index.html</code> to show you what I mean:</p>
<pre class="language-markdown"><code class="language-markdown"><span class="highlight-line"><span class="token hr punctuation">---</span></span><br><span class="highlight-line">layout: layout.liquid</span><br><span class="highlight-line"><span class="token title important">pageTitle: Welcome to my blog</span><br><span class="highlight-line"><span class="token punctuation">---</span></span></span><br><span class="highlight-line">{% for post in collections.posts %}</span><br><span class="highlight-line"><span class="token code keyword"> &lt;h2>&lt;a href="{{ post.url }}">{{ post.data.pageTitle }}&lt;/a>&lt;/h2></span></span><br><span class="highlight-line"><span class="token code keyword"> &lt;em>{{ post.date | date: "%Y-%m-%d" }}&lt;/em></span></span><br><span class="highlight-line">{% endfor %}</span></code></pre>
<ol>
<li>Front matter: We’re using the same <code>layout</code> and assigning a <code>pageTitle</code> used in the layout file.</li>
<li>Iterate over our blog posts collection using the Liquid <code>for</code> tag (note the new syntax using <code>%</code>’s): <code>{% for post in collections.posts %}</code> Read more about <a href="https://shopify.github.io/liquid/tags/iteration/#for">Liquid <code>for</code> tags</a>.</li>
<li><code>post.url</code> has the URL string of each blog post entry.</li>
<li><code>post.data</code> has all the data (from all data sources including front matter and data files) used in each blog post (e.g. <code>post.data.pageTitle</code>)</li>
<li><code>post.date</code> has each blog post’s file creation date. (Read more about <a href="https://www.11ty.io/docs/dates/">Content Dates</a>)</li>
</ol>
<p>This outputs the following:</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token doctype">&lt;!doctype html></span></span><br><span class="highlight-line"><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">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <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">=</span><span class="token punctuation">"</span>utf-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>title</span><span class="token punctuation">></span></span>My Blog<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>title</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">></span></span>Welcome to my blog<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"></span><br><span class="highlight-line"> <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>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/posts/blog-post/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>This is my Title<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>h2</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">></span></span>2018-12-28<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"></span><br><span class="highlight-line"> <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>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/posts/blog-post-2/<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>This is my Title 2<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>h2</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">></span></span>2018-12-28<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span></span></code></pre>
<p>You did it! It’s a blog!</p>
<p>To take this a bit further, check out the <a href="https://github.com/11ty/eleventy-base-blog"><code>eleventy-base-blog</code> project</a>, which is a full starter project for a blog site including RSS feeds, tag pages, syntax highlighting, custom markdown plugins, and more!</p>
<h2 id="put-it-on-the-web!">Put it on the web! <a class="direct-link" href="#put-it-on-the-web!" aria-hidden="true">#</a></h2>
<p>You can use Netlify to very quickly put this on the web. Register and/or Log in to <a href="https://app.netlify.com/"><code>app.netlify.com</code></a> and drag and drop our <code>_site</code> folder onto the web browser window to upload the contents live to the web!</p>
<p>It happens almost instantly.</p>
<p>Check out our <a href="https://modest-colden-60bed7.netlify.com/">simple blog demo</a> on Netlify.</p>
<h2 id="go-forth-and-eleventy">Go forth and Eleventy <a class="direct-link" href="#go-forth-and-eleventy" aria-hidden="true">#</a></h2>
<p>Read more at the <a href="https://www.11ty.io/docs/">official Eleventy documentation</a>.</p>
<p>But most importantly, don’t forget to send us a link to your new blog! Hit us up on Twitter <a href="https://twitter.com/filamentgroup">@filamentgroup</a> with any feedback!</p>
Styling a Select Like It's 20192018-12-17T19:00:00-05:00<h1>Styling a Select Like It’s 2019</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> 12/18/2018</p>
<p class="note"><strong>Update 12/19</strong> The select now has totally consistent appearance in Internet Explorer 11 and 10, thanks to <a href="https://twitter.com/jelmerdemaat/status/1075373995313324032">a nice tip</a> from <a href="https://twitter.com/jelmerdemaat">Jelmer de Maat</a></p>
<p>The <code>select</code> element has long been difficult to style consistently across browsers.
To avoid its shortcomings in the past, we have used workarounds like styling a parent element, adding pseudo-elements,
and even using JavaScript to construct a select-like control out of different elements that are easier to style. But workarounds are hard to maintain and use, not to mention the accessibility challenges that custom elements bring.</p>
<p>Recently, we’d seen some articles suggest that things haven’t changed a great deal with <code>select</code>'s styling limitations,
but I decided to return to the problem and tinker with it myself to be sure. As it turns out, a reasonable set of styles can create a consistent and attractive <code>select</code> across new browsers, while remaining just fine in older ones too.</p>
<h2 id="a-quick-example">A Quick Example <a class="direct-link" href="#a-quick-example" aria-hidden="true">#</a></h2>
<p>First, for background, here’s an <em>unstyled</em> <code>select</code> element. It’s going to look a bit different depending on the browser you’re using.</p>
<select>
<option>This is a native select element</option>
<option>Apples</option>
<option>Bananas</option>
<option>Grapes</option>
<option>Oranges</option>
</select>
<p>Below is that same <code>select</code> element styled directly with some CSS. No additional wrapper elements or pseudo-elements are involved (except for one that’s needed for IE10+):</p>
<div>
<style>
.select-css {
display: block;
font-size: 16px;
font-family: sans-serif;
font-weight: 700;
color: #444;
line-height: 1.3;
padding: .6em 1.4em .5em .8em;
width: 100%;
max-width: 100%;
box-sizing: border-box;
margin: 0;
border: 1px solid #aaa;
box-shadow: 0 1px 0 1px rgba(0,0,0,.04);
border-radius: .5em;
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
background-color: #fff;
background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23007CB2%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E'),
linear-gradient(to bottom, #ffffff 0%,#e5e5e5 100%);
background-repeat: no-repeat, repeat;
background-position: right .7em top 50%, 0 0;
background-size: .65em auto, 100%;
}
.select-css::-ms-expand {
display: none;
}
.select-css:hover {
border-color: #888;
}
.select-css:focus {
border-color: #aaa;
box-shadow: 0 0 1px 3px rgba(59, 153, 252, .7);
box-shadow: 0 0 0 3px -moz-mac-focusring;
color: #222;
outline: none;
}
.select-css option {
font-weight:normal;
}
</style>
</div>
<select class="select-css">
<option>This is a native select element</option>
<option>Apples</option>
<option>Bananas</option>
<option>Grapes</option>
<option>Oranges</option>
</select>
<p>You can find some examples of how it looks in different layout contexts over here on the <a href="http://filamentgroup.github.io/select-css/demo/">project demo page</a>.</p>
<h2 id="the-code">The Code <a class="direct-link" href="#the-code" aria-hidden="true">#</a></h2>
<p>The HTML for the <code>select</code> above is shown below. Note that the CSS applies to any <code>select</code> with a class of <code>select-css</code>.</p>
<pre class="language-html"><code class="language-html"><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>select</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>select-css<span class="token punctuation">"</span></span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span><span class="token punctuation">></span></span>This is a native select element<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span><span class="token punctuation">></span></span>Apples<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span><span class="token punctuation">></span></span>Bananas<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span><span class="token punctuation">></span></span>Grapes<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>option</span><span class="token punctuation">></span></span>Oranges<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>option</span><span class="token punctuation">></span></span></span><br><span class="highlight-line"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>select</span><span class="token punctuation">></span></span></span></code></pre>
<p>And here’s the CSS driving the <code>select</code>'s appearance, which you can also find in <a href="https://github.com/filamentgroup/select-css/blob/master/src/select-css.css">our select-css repo</a>. I’ll add some notes about certain parts just after it as well.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.select-css</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">font-size</span><span class="token punctuation">:</span> 16px<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">font-family</span><span class="token punctuation">:</span> sans-serif<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">font-weight</span><span class="token punctuation">:</span> 700<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> #444<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">line-height</span><span class="token punctuation">:</span> 1.3<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">padding</span><span class="token punctuation">:</span> .6em 1.4em .5em .8em<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">max-width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span> </span><br><span class="highlight-line"> <span class="token property">box-sizing</span><span class="token punctuation">:</span> border-box<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">border</span><span class="token punctuation">:</span> 1px solid #aaa<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 1px 0 1px <span class="token function">rgba</span><span class="token punctuation">(</span>0,0,0,.04<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">border-radius</span><span class="token punctuation">:</span> .5em<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">-moz-appearance</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">-webkit-appearance</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">appearance</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">background-color</span><span class="token punctuation">:</span> #fff<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token url">url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23007CB2%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E')</span>,</span><br><span class="highlight-line"> <span class="token function">linear-gradient</span><span class="token punctuation">(</span>to bottom, #ffffff 0%,#e5e5e5 100%<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">background-repeat</span><span class="token punctuation">:</span> no-repeat, repeat<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">background-position</span><span class="token punctuation">:</span> right .7em top 50%, 0 0<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">background-size</span><span class="token punctuation">:</span> .65em auto, 100%<span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span><br><span class="highlight-line"><span class="token selector">.select-css::-ms-expand</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span><br><span class="highlight-line"><span class="token selector">.select-css:hover</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token property">border-color</span><span class="token punctuation">:</span> #888<span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span><br><span class="highlight-line"><span class="token selector">.select-css:focus</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token property">border-color</span><span class="token punctuation">:</span> #aaa<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 0 1px 3px <span class="token function">rgba</span><span class="token punctuation">(</span>59, 153, 252, .7<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 0 0 3px -moz-mac-focusring<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">color</span><span class="token punctuation">:</span> #222<span class="token punctuation">;</span> </span><br><span class="highlight-line"> <span class="token property">outline</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span><br><span class="highlight-line"><span class="token selector">.select-css option</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token property">font-weight</span><span class="token punctuation">:</span>normal<span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>You can find the CSS on (Github)[<a href="https://github.com/filamentgroup/select-css/blob/master/src/select-css.css">https://github.com/filamentgroup/select-css/blob/master/src/select-css.css</a>] or <a href="https://www.npmjs.com/package/fg-select-css">NPM</a>.</p>
<h3 id="notes-on-the-css">Notes on the CSS <a class="direct-link" href="#notes-on-the-css" aria-hidden="true">#</a></h3>
<p>The CSS for this is fine to use as-is, but if you’re editing it at all, you might want to be aware of a few numbers and values that help it look right.</p>
<ul>
<li>The <code>select</code> is set to <code>display: block</code> by default but you can style it <code>display: inline-block; width: auto;</code> if you’d like it to sit alongside a <code>label</code>.</li>
<li>The background of the <code>select</code> is created using two background images: the first is an svg arrow icon (expressed inline as a data URI) and the second is a repeating linear gradient. Either URL could be an external image if you’d like. If you change the icon image, be aware that its size is set in the first section of the later <code>background-size: .65em auto, 100%;</code> property. And its position is set via <code>background-position: right .7em top 50%, 0 0;</code> (which is <code>.7em</code> from the right side, respectively). Also, if the size changes, you might want to make more right padding on the button so that it doesn’t overlap the <code>select</code>'s text, but be aware that in IE9 and older, the custom arrow will not appear, and the browser’s default arrow will show to the left of the padding, so don’t add too much there or IE9’s arrow will be inset really far.</li>
<li>The linear gradient background is important to keep, because its presence actually prevents IE9 and older from recognizing the background property, and as a result it won’t show the custom icon alongside its unhideable native one. <strong>If you want a flat color, use a linear gradient between two of the same color values.</strong></li>
<li>The <code>appearance</code> rule and its and prefixed versions are important to unset some default browser <code>select</code> styling.</li>
<li>The <code>font-size: 16px;</code> rule is important because iOS Safari will zoom-in the site layout if the <code>select</code>'s text is less than 16px. Generally, this behavior is annoying so we try to avoid it with a 16px font size on <code>select</code>s.</li>
<li>The <code>.select-css option</code> keeps <code>option</code> elements from inheriting the bold font weight of the <code>select</code> button itself.</li>
<li>As noted in <a href="https://scottaohara.github.io/a11y_styled_form_controls/src/select/">Scott O’Hara’s article</a>, setting background color on the <code>select</code> (though not background <em>image</em> as I’ve used here, can cause <code>option</code> elements to inherit background colors as well, which can cause problems. So avoid doing that. )</li>
<li>The <code>.select-css::-ms-expand</code> rule instructs IE11 and IE10 to hide the menu icon pseudo element, so the custom icon behind it can appear. Thanks for the tip, Jelmer de Maat.</li>
</ul>
<h2 id="how-it-looks-across-browsers">How it looks across browsers <a class="direct-link" href="#how-it-looks-across-browsers" aria-hidden="true">#</a></h2>
<p>Here are some snapshots of the <code>select</code> in various browsers. In some browsers, like IE9 and older, the icon design doesn’t totally hold up but the control is usable and looks fine enough for our usual purposes.</p>
<ul>
<li>Firefox <br> <img src="/images/select-css/firefox.png" alt=""></li>
<li>Chrome <br> <img src="/images/select-css/chrome.png" alt=""></li>
<li>Safari <br> <img src="/images/select-css/safari.png" alt=""></li>
<li>Edge <br> <img src="/images/select-css/Edge.png" alt=""></li>
<li>Internet Explorer 11 <br> <img src="/images/select-css/IE11.png" alt=""></li>
<li>Internet Explorer 10 <br> <img src="/images/select-css/IE10.png" alt=""></li>
<li>Internet Explorer 9 <br> <img src="/images/select-css/IE9.png" alt=""></li>
<li>Internet Explorer 8 <br> <img src="/images/select-css/ie8.png" alt=""></li>
<li>iOS Safari <br> <img src="/images/select-css/iOS.png" alt=""></li>
<li>Android Chrome <br> <img src="/images/select-css/android%20chrome.png" alt=""></li>
</ul>
<h2 id="enjoy!">Enjoy! <a class="direct-link" href="#enjoy!" aria-hidden="true">#</a></h2>
<p>And that’s about it. Thanks for reading. If you have any feedback, or just want to say hello, give us a shout on <a href="https://twitter.com/filamentgroup">twitter at @filamentgroup</a>.</p>
Should I Use JavaScript to Load My Web Fonts?2018-11-28T19:00:00-05:00<h1>Should I Use JavaScript to Load My Web Fonts?</h1>
<p>Posted by <a href="/about/#zach-leatherman">Zach</a> 11/29/2018</p>
<p>When they were first introduced, web fonts were primarily a CSS feature, and for many web developers they’ve always loaded them using CSS (and nothing more). But in the last decade, default web font loading behavior in many browsers made CSS-only methods a gamble with our page’s text render, and thoughtful developers switched to safer JavaScript methods. Recently, browser support for new and safer CSS-only strategies have left some developers wondering: are JavaScript methods to load web fonts necessary? Are they useful? Let’s dig in.</p>
<h2 id="the-epochs-of-responsible-web-font-loading">The Epochs of Responsible Web Font Loading <a class="direct-link" href="#the-epochs-of-responsible-web-font-loading" aria-hidden="true">#</a></h2>
<h3 id="1997%3A-css-only-web-fonts-were-good">1997: CSS-only web fonts were good <a class="direct-link" href="#1997%3A-css-only-web-fonts-were-good" aria-hidden="true">#</a></h3>
<p>Internet Explorer was the only browser that supported web fonts and did so in a progressive-enhancement friendly way that—in hindsight—seems remarkably forward-thinking. When a web font loads in Internet Explorer, the fallback text is visible during load: robust and readable.</p>
<h3 id="2008%3A-javascript-for-web-fonts-recommended">2008: JavaScript for web fonts recommended <a class="direct-link" href="#2008%3A-javascript-for-web-fonts-recommended" aria-hidden="true">#</a></h3>
<p>Web font support was added to Safari in 2008. Unfortunately, Safari chose to make all web font text invisible text while the font file was loading. Notably this invisible text did not include a sane timeout for these requests—turning web font requests into a single point of failure for text on the page. If your request for the font file stalled—your text could be invisible indefinitely offering little recourse to the user except to start over and reload the entire page. As such, <a href="/lab/font-events.html">JavaScript methods</a> were needed to work around this problem and to load web fonts responsibly.</p>
<h3 id="2016%3A-css-only-web-fonts-weren%E2%80%99t-terrible">2016: CSS-only web fonts weren’t terrible <a class="direct-link" href="#2016%3A-css-only-web-fonts-weren%E2%80%99t-terrible" aria-hidden="true">#</a></h3>
<p>Firefox was the first web browser to introduce a invisibility timeout to their font loading in 2011—if the web font hasn’t loaded in three seconds, the fallback font is rendered. Chrome followed suit in 2014 and Safari was the last major browser to adopt the timeout in 2016. Notably, this marked the year in which CSS-only methods to load web fonts were no longer a single point of failure for web pages. However, developers still had limited control over text visibility while the font was loading.</p>
<p><em><a href="https://www.zachleat.com/web/fout-foit-history/">Read a more detailed account of web font loading history.</a></em></p>
<h3 id="2018%3A-css-only-is-good-again!">2018: CSS-only is good again! <a class="direct-link" href="#2018%3A-css-only-is-good-again!" aria-hidden="true">#</a></h3>
<p>2018 marked a new epoch of font loading behavior with the adoption of <code>font-display</code>. Our CSS-only savior, <code>font-display</code> is a simple CSS descriptor that allows developers to take back control over the text invisibility behavior of web font loading.</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token atrule"><span class="token rule">@font-face</span></span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token property">font-family</span><span class="token punctuation">:</span> Noto Serif<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">src</span><span class="token punctuation">:</span> <span class="token url">url(notoserif.woff2)</span> <span class="token function">format</span><span class="token punctuation">(</span><span class="token string">'woff2'</span><span class="token punctuation">)</span>,</span><br><span class="highlight-line"> <span class="token url">url(notoserif.woff)</span> <span class="token function">format</span><span class="token punctuation">(</span><span class="token string">'woff'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token property">font-display</span><span class="token punctuation">:</span> swap<span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>With <a href="https://caniuse.com/#feat=css-font-rendering-controls">burgeoning browser support</a>, <code>font-display</code> alleviates JavaScript as a requirement for visible text while the font loads! This makes for a great, simple baseline for font loading that will be “good enough” for many sites.</p>
<p>Learn more at <a href="https://font-display.glitch.me/">Monica Dinculescu’s <code>font-display</code> page on Glitch</a>.</p>
<h2 id="why-would-you-still-use-javascript%3F">Why Would You Still Use JavaScript? <a class="direct-link" href="#why-would-you-still-use-javascript%3F" aria-hidden="true">#</a></h2>
<p>It is true that using the <code>font-display</code> descriptor can get you to an acceptable level of web font loading—but we can do better! Let’s look at some additional improvements we can make:</p>
<h3 id="1.-group-repaints">1. Group Repaints <a class="direct-link" href="#1.-group-repaints" aria-hidden="true">#</a></h3>
<p>When you load multiple font files for a single typeface, each request can incur a separate repaint and reflow. Reduce the amount of movement on your page by grouping this into a single repaint. You can read more about this at <a href="https://www.zachleat.com/web/font-display-reflow/">The Problem with <code>font-display</code> and Reflow</a>.</p>
<p>Your JavaScript for this might look something like:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token string">"fonts"</span> <span class="token keyword">in</span> document<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token keyword">var</span> font <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FontFace</span><span class="token punctuation">(</span></span><br><span class="highlight-line"> <span class="token string">"Noto Serif"</span><span class="token punctuation">,</span></span><br><span class="highlight-line"> <span class="token string">"url(notoserif.woff2) format('woff2'), url(notoserif.woff) format('woff')"</span></span><br><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"> <span class="token keyword">var</span> fontBold <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FontFace</span><span class="token punctuation">(</span></span><br><span class="highlight-line"> <span class="token string">"Noto Serif"</span><span class="token punctuation">,</span></span><br><span class="highlight-line"> <span class="token string">"url(notoserif-bold.woff2) format('woff2'), url(notoserif-bold.woff) format('woff')"</span><span class="token punctuation">,</span></span><br><span class="highlight-line"> <span class="token punctuation">{</span> weight<span class="token punctuation">:</span> <span class="token string">"700"</span> <span class="token punctuation">}</span></span><br><span class="highlight-line"> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"> Promise<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">[</span></span><br><span class="highlight-line"> font<span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span></span><br><span class="highlight-line"> fontBold<span class="token punctuation">.</span><span class="token function">load</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span><br><span class="highlight-line"> <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>loadedFonts<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"> <span class="token comment">// Render them at the same time</span></span><br><span class="highlight-line"> loadedFonts<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span>font<span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> document<span class="token punctuation">.</span>fonts<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>font<span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<h3 id="2.-adapt-to-user-preferences">2. Adapt to User Preferences <a class="direct-link" href="#2.-adapt-to-user-preferences" aria-hidden="true">#</a></h3>
<p>You can use the <code>Save-Data</code> preference to opt-out of fonts when the user has enabled Data Saver mode. Read more at <a href="https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/save-data/">Delivering Fast and Light Applications with Save-Data</a> on Google’s Web Fundamentals.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">function</span> <span class="token function">loadFonts</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">/* NOTE: Reuse the Group Repaints code snippet above, here */</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token keyword">if</span><span class="token punctuation">(</span> navigator<span class="token punctuation">.</span>connection <span class="token operator">&amp;&amp;</span></span><br><span class="highlight-line"> navigator<span class="token punctuation">.</span>connection<span class="token punctuation">.</span>saveData <span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token function">loadFonts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Or, you can use the <code>prefers-reduced-motion</code> preference (<a href="https://caniuse.com/#feat=prefers-reduced-motion">Browser support</a>) to opt-out of web font loading when the user has enabled the <code>Reduce Motion</code> accessibility option.</p>
<p>To enable this on Mac OS, go to System Preferences &gt; Accessibility &gt; Reduce Motion. On <a href="https://support.apple.com/en-us/HT202655">iOS it’s Settings &gt; General &gt; Accessibility &gt; Reduce Motion</a>.</p>
<p>It might be a stretch to say that web font reflow is motion—but it would be an improvement to page stability. Read more at <a href="https://css-tricks.com/introduction-reduced-motion-media-query/">An Introduction to the Reduced Motion Media Query</a> on CSS-Tricks.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">function</span> <span class="token function">loadFonts</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">/* NOTE: Reuse the Group Repaints code snippet above, here */</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token keyword">if</span><span class="token punctuation">(</span> <span class="token string">"matchMedia"</span> <span class="token keyword">in</span> window <span class="token operator">&amp;&amp;</span></span><br><span class="highlight-line"> window<span class="token punctuation">.</span><span class="token function">matchMedia</span><span class="token punctuation">(</span><span class="token string">"(prefers-reduced-motion: reduce)"</span><span class="token punctuation">)</span><span class="token punctuation">.</span>matches <span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">// do nothing</span></span><br><span class="highlight-line"><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token function">loadFonts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>For extra credit, I could imagine using the Paint Timing API to only opt-out of rendering if the web font finishes loading after the <code>first-paint</code> entry has been logged. Read more at the lovely post: <a href="https://jmperezperez.com/paint-timing-api/">PerformanceObserver and Paint Timing API</a> written by José M. Pérez. But truthfully this might be a little much, even for me.</p>
<h3 id="3.-adapt-to-user-context">3. Adapt to User Context <a class="direct-link" href="#3.-adapt-to-user-context" aria-hidden="true">#</a></h3>
<p>You can also use the <a href="http://wicg.github.io/netinfo/">Network Information API</a> (<a href="https://caniuse.com/#feat=netinfo">Browser support</a>) to opt out of fonts on slow connections. Read more at the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Network_Information_API">Network Information API documentation</a> on the MDN web docs.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">function</span> <span class="token function">loadFonts</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">/* NOTE: Reuse the Group Repaints code snippet above, here */</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token keyword">if</span><span class="token punctuation">(</span> navigator<span class="token punctuation">.</span>connection <span class="token operator">&amp;&amp;</span></span><br><span class="highlight-line"> <span class="token punctuation">(</span> navigator<span class="token punctuation">.</span>connection<span class="token punctuation">.</span>effectiveType <span class="token operator">===</span> <span class="token string">"slow-2g"</span> <span class="token operator">||</span></span><br><span class="highlight-line"> navigator<span class="token punctuation">.</span>connection<span class="token punctuation">.</span>effectiveType <span class="token operator">===</span> <span class="token string">"2g"</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">// do nothing</span></span><br><span class="highlight-line"><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token function">loadFonts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Note that you may be tempted to subscribe to the <code>&quot;change&quot;</code> event exposed in the Network Information API. Careful! You don’t want your web font load to trigger super-late after the user may already be knee-deep in reading! The later a web font reflow happens, the more disruptive it feels.</p>
<h3 id="4.-using-third-party-hosts">4. Using Third Party Hosts <a class="direct-link" href="#4.-using-third-party-hosts" aria-hidden="true">#</a></h3>
<p>Unfortunately at time of writing no known third party web font host supports the CSS-only <code>font-display</code> descriptor. I’d recommend sticking with JavaScript approaches when using these services for now.</p>
<h2 id="javascript-isn%E2%80%99t-going-away">JavaScript isn’t going away <a class="direct-link" href="#javascript-isn%E2%80%99t-going-away" aria-hidden="true">#</a></h2>
<p>As you can see, the advanced web font loading control offered by JavaScript still provides more than sufficient value to keep it around. You can adapt your page’s performance profile to suit a user’s network conditions, user preferences, improving the general loading behavior of self hosted fonts and third party hosting providers.</p>
<p>Do you have another idea? Let us know <a href="https://twitter.com/filamentgroup">@filamentgroup on Twitter</a>.</p>
A/B Testing at the Edge with Servers Workers2018-11-26T19:00:00-05:00<h1>A/B Tests at the Edge with Servers Workers</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> 11/27/2018</p>
<p>In <a href="/lab/second-meaningful-content.html">Second Meaningful Content: the Worst Performance Metric</a>, I noted the terrible impact on page loading performance that can occur when sites introduce personalized content and A/B test logic on the client-side, using JavaScript. To recap, the delivery and application of this pattern tends to produce a “wait and switch” effect; delaying both initial page rendering, and the subsequent re-rendering of portions of the page, as illustrated in this visual timeline:</p>
<p><img src="/images/2ndmeaningfulcontent.png" alt="timeline showing first and second meaningful content, with a long delay"></p>
<p>The article finished with some tips for either mitigating the impact of the approach, or moving the logic away from the browser. However, while the latter is ideal, it may not be an easy change to make. Recently, we’ve been exploring an approach that gives us all the performance benefits of a server-side approach with the familiarity of doing the work with JavaScript.</p>
<h2 id="servers-workers-to-the-rescue%3F">Servers Workers to the rescue? <a class="direct-link" href="#servers-workers-to-the-rescue%3F" aria-hidden="true">#</a></h2>
<p>Delivering HTML that’s ready to render on arrival is a worthy goal. However, if that HTML needs to vary its content for different users, achieving that goal will require running some logic on the server or in middleware between the server and the browser. Using middleware for content transformations like this is not a new thing, but recently CDN-provider Cloudflare introduced support on their middleware CDN servers using JavaScript Service Workers to handle the logic, which is a new idea indeed.</p>
<p><a href="https://www.cloudflare.com/products/cloudflare-workers/"><img alt="Cloudflare Workers Site" src="/images/serversworkers.png" style="border:1px solid #eee;"></a></p>
<p>For those not already familiar, Service Workers are JavaScript files that are typically sent to the browser where they act as a proxy to handle tasks like caching, content modifications, and offline fallbacks. In typical use, Service Workers have some differences in browser support, and can be used only after downloading, meaning they’re more helpful after an initial page visit. However, running Service Workers in a server environment (or middleware like Cloudflare’s CDN) eliminates many of those technical concerns. For those familiar with authoring Service Workers, we can use the same patterns and syntax when running service workers in the browser but take all the advantages of being on the server.</p>
<p>More like <em>servers workers</em>… right?</p>
<h2 id="working-with-servers-workers">Working with Servers Workers <a class="direct-link" href="#working-with-servers-workers" aria-hidden="true">#</a></h2>
<p>Given that we have clients who use Cloudflare already, and that we know a bit about Service Workers ourselves, we were interested in trying this tool out. On <a href="https://cloudflareworkers.com/">Cloudflare’s site, there’s a nice playground page</a> to test how these servers workers <em>work</em>. You can try plugging in most any URL and practice modifying its response text, swapping image URLs, changing headers and more. To get a little familiarity, I’d suggest going to the playground site and browsing the example workers in their documentation.</p>
<p><a href="https://cloudflareworkers.com/"><img alt="Cloudflare's Edge Workers Playground" src="/images/serversworkers-playground.png" style="border:1px solid #eee;"></a></p>
<p>Let’s explore a couple of examples of how we’ve been trying to use this tool to move dynamic content logic out of the browser to improve performance.</p>
<h3 id="a-delicious-a%2Fb-test">A Delicious A/B Test <a class="direct-link" href="#a-delicious-a%2Fb-test" aria-hidden="true">#</a></h3>
<p>At the recent <a href="https://perfnow.nl/">Performance.Now() conference</a>, I demonstrated a couple of service workers that we built to drive A/B tests. I’ll start with a simpler example before moving on to a more complex, but more useful one.</p>
<p>Pictured below, we have two variations of a link to a site’s “Treats” section that we could serve to our users to see if we get more clicks when the text is either “donuts” or “stroop wafels” (<em>the conference was in Amsterdam, after all!</em>).</p>
<p><img src="/images/serversworkers-ab1.png" alt="Simple AB Test buttons"></p>
<p>In this A/B test, we can consider the users who get “donuts” to be in the A group, while the B group will be the <em>stroop group</em>. To drive this, we could start with <a href="https://master-origin-servers-workers.fgview.com/simple.html">some HTML</a> containing a link that looks like this, containing the “donuts” text:</p>
<pre><code>
&lt;a href="/treats">Try our delicious &lt;i>donuts!&lt;/i>&lt;/a>
</code></pre>
<p>The following Service Worker script, when running on Cloudflare and pointed at that page, would change the first instance of the text “donuts” to instead say, “stroop wafels”. Note the line where a standard JavaScript <code>replace</code> method is used to swap the text.</p>
<pre><code>
addEventListener("fetch", event => {
event.respondWith(fetchAndModify(event.request));
});
async function fetchAndModify(request) {
const response = await fetch(request);
const text = await response.text();
<strong> const modified = text.replace("donuts", "stroop wafels");</strong>
return new Response(modified, {
status: response.status,
statusText: response.statusText,
headers: response.headers
});
}
</code></pre>
<p>Concise and standards-based!</p>
<p>Of course, to actually be useful as an A/B test, we’d want that worker to do a little more.</p>
<ul>
<li>First, it should <em>only</em> transform the text for 50% of our visitors.</li>
<li>Second, we’d want it to only run on requests to a particular HTML file on our site (rather than running on every request to files on the site - including requests to CSS, images, etc!).</li>
<li>Third, we’d want to give the test a name and track which group “wins” the test, for example, by setting a cookie for the group that the user belongs to. This cookie could later be detected by most any analytics package to correlate, for example, sales from each group. I should also note here that once a user is <em>cookied</em> into a given test group, the worker will continue to use that same group on subsequent visits to the same page.</li>
</ul>
<p>Rather than dump the full code into this article, I’ll link out to a playground page containing a Service Worker that performs the above list of tasks: <a href="https://cloudflareworkers.com/#a54c7d6e665ba479c5f7b358ac61edfd:https://master-origin-servers-workers.fgview.com/simple.html">A/B Service Worker Example 1</a>.</p>
<p>If you open that page, you’ll see some configuration at the top of the service worker for the test’s name, the text to look for, the text to potentially swap in, and the URL that the test should run on. Browsing the code further, you’ll see the parts that don’t require editing, where the worker handles the 50/50 split, setting a cookie with the test name and assigned group.</p>
<p>And that’s it! Here’s an example of the link that the Stroop Group will see, all pre-transformed when the browser recieves it:</p>
<p><a href="https://cloudflareworkers.com/#a54c7d6e665ba479c5f7b358ac61edfd:https://master-origin-servers-workers.fgview.com/simple.html"><img alt="Simple AB Test buttons: output as the B group" src="/images/serversworkers-ab2.png" style="border:1px solid #eee;"></a></p>
<h3 id="a-more-delicious-swap">A More Delicious Swap <a class="direct-link" href="#a-more-delicious-swap" aria-hidden="true">#</a></h3>
<p>The worker above handles a simple A/B test just fine, but the transformation it does is very limited. More often, we’ll want to change more than just a word, but instead swap between larger slags of HTML. For example, a site might A/B test an entire hero banner on a landing page, and suffice to say, a lowly word replacement is really under equipped for matching multiline HTML like that.</p>
<p>So to build on our first example, let’s consider a more complex case, such as this link swap that changes not only the text but an image and some styling as well:</p>
<p><img src="/images/serversworkers-ab3.png" alt="More complex AB test example buttons"></p>
<p>For cases like this that require larger swaps of HTML, we made a more robust worker. What’s neat about this one is it requires no JavaScript configuration for each test. Instead, the configuration of one or more tests occurs inside the HTML that the service worker runs on, denoted by special HTML comments that mark the start and end of each named group.</p>
<p>Take this initial markup for example:</p>
<pre><code>
&lt;!--workertest:mytest=a-->
&lt;a href="#" class="donuts">
Try our delicious &lt;i>donuts!&lt;/i>
&lt;img src="donuts.png" alt="two iced donuts">
&lt;/a>
&lt;!--/workertest:mytest=a-->
&lt;!--workertest:mytest=b-->
&lt;a href="#" class="wafel">
Try our delicious &lt;i>stroop wafels!&lt;/i>
&lt;img src="wafel.png" alt="stroop wafels">
&lt;/a>
&lt;!--/workertest:mytest=b-->
</code></pre>
<p>Given the HTML above, our service worker will determine (based on the comments that start with the <code>workertest:</code> prefix) that there’s an A/B test in play called “mytest”. It will perform the same logic as the first simple service worker example with regards to dividing the visitors into cookied groups, but instead of a simple text swap, this worker will actually <em>delete</em> the HTML for the group that does not apply to the visitor and return the HTML without that markup, so group A will receive <em>only</em> the markup for the “donuts” link, and the opposite for group B.</p>
<p>So you can see how it works, here’s a playground page running that worker on a page containing that markup above: <a href="https://cloudflareworkers.com/#2e5a08efcc12a771ff124254057903e3:https://master-origin-servers-workers.fgview.com/complex.html">A/B Service Worker Example 2</a></p>
<p><a href="https://cloudflareworkers.com/#2e5a08efcc12a771ff124254057903e3:https://master-origin-servers-workers.fgview.com/complex.html"><img alt="More complex example of AB Test buttons: output as the B group" src="/images/serversworkers-ab4.png" style="border:1px solid #eee;"></a></p>
<h2 id="more-work-to-do">More <em>Work</em> To Do <a class="direct-link" href="#more-work-to-do" aria-hidden="true">#</a></h2>
<p>This post covered some ways we can use Cloudflare’s Service Worker support (or, <em>servers workers</em> if you’d like!) to move costly clientside A/B tests out of the browser to a middle layer where they can happen before the page is delivered. We think this tool is pretty neat, and given the standards-based nature of the code itself, we hope other CDN providers pick up on this and support it as well.</p>
<p>But more generally, the takeaway here is that we should be thinking about how to move these sorts of tests out of the browser, and not necessarily with Cloudflare’s worker support (which are just one tool), but by any means available.
It’s often much faster to change the initial HTML on the server than it is to use many of the popular tools that do this in the browser, and thankfully many of these same tools offer ways to use them on the server instead. We should push for the workflow that doesn’t bog down the browser every time.</p>
<p>Thanks for reading, and if you have any feedback, feel free to <a href="https://twitter.com/filamentgroup">get in touch with us on Twitter</a>.</p>
Second Meaningful Content: the Worst Performance Metric2018-11-18T19:00:00-05:00<h1>Second Meaningful Content: the Worst Performance Metric</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> 11/19/2018</p>
<p>Personalized and A/B content tests are common features of many websites today because they can help teams improve conversion and engagement. In recent years, however, we’ve seen many sites move these dynamic content workflows from the server-side to the client-side to make it easier for marketing and UX teams to work with less developer support. Unfortunately, this shift to client-side JS can have a large impact on performance.</p>
<h2 id="the-old-wait-%26-switch-gag">The Old Wait &amp; Switch Gag <a class="direct-link" href="#the-old-wait-%26-switch-gag" aria-hidden="true">#</a></h2>
<p>To recap a typical implementation of this pattern: the server delivers usable HTML content along with the some JavaScript that will block the page from rendering while the script(s) loads and evaluates. That blocking period is the first performance antipattern of the workflow, but a more egregious one follows: once the script executes, it will remove a piece of content from the page and replace it with a new piece of content in its place. That new content might be more targeted to a given user, but the time and complexity involved in delivering it can mean long delays for users. Ironically, the opportunity to increase conversion with these tools may come at the cost of some users’ ability to use the site at all.</p>
<p>This pattern leads to such a unique effect on page load that at last week’s Perf.Now() Conference, <a href="https://twitter.com/scottjehl/status/1061985477694316544">I coined a new somewhat tongue-in-cheek performance metric</a> called <strong>“Second Meaningful Content,”</strong> which occurs long after the handy <em>First Meaningful Content</em> (or <a href="https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint">Paint</a>) metric, which would otherwise mark the time at which a page starts to appear usable.</p>
<p>A visual timeline of the pattern tends to look like this:</p>
<p><img src="/images/2ndmeaningfulcontent.png" alt="timeline showing first and second meaningful content, with a long delay"></p>
<p>This pattern can take a formerly fast, optimized site and <em>render</em> it (heh, sorry) painfully slow; we’ve seen sites with pages that used to be <em>interactive</em> in 4 or 5 seconds on an average phone and network suddenly taking 12 seconds to even begin to <em>look</em> usable.</p>
<p>Part of the reason for this delay is that JavaScript takes a long time to process on smartphones today, even if it downloads fast. For example, <a href="https://medium.com/@addyosmani/the-cost-of-javascript-in-2018-7d8950fbb5d4">the average new smartphone currently takes 2 seconds to process 200kb of compressed JavaScript</a>, which just happens to be the size of the A/B JavaScript in use on a site I’m auditing right this moment. Another part of the delay is that sometimes these scripts will kick off additional blocking requests to the server while the user waits, introducing even more cycles of downloading and evaluating scripts.</p>
<h2 id="ways-to-mitigate">Ways to Mitigate <a class="direct-link" href="#ways-to-mitigate" aria-hidden="true">#</a></h2>
<p>This pattern has become so commonly integrated on top of sites nowadays that it seems it’s now easier to make a fast website than it is to keep a website fast. So what can we do about it?</p>
<ul>
<li><strong>Only A/B test repeat views:</strong> first we can recommend that site owners use these client-side tools in less-critical situations so that they cause less damage. For example, they could choose to not vary the content for first-time visitors, and instead just preload the scripts so that they’re cached and ready for use in subsequent page views. Of course, having the scripts cached will only help with the <em>loading</em> time. The time the scripts spend actually executing <em>after they’re loaded</em> is the more problematic and harmful delay.</li>
<li><strong>Preload the Inevitable:</strong> Using <code>link[rel=preload]</code> elements in the <code>head</code> of an HTML document to preload scripts that will be requested at a later time can dramatically improve loading time. Once again, this won’t change the delays incurred by evaluating and running the scripts after they download, but it can save seconds in upfront load time.</li>
<li><strong>Only A/B content below the fold and (ideally) delay the scripts:</strong> Along similar lines, we could recommend that dynamic content is only used for portions of a page that are outside of the viewport when the page first loads, far down the page so that users may not see the flash of unswapped content happen. If this use case is possible, it might be worth also considering whether the blocking scripts can be referenced and loaded later as well, perhaps just before the piece of content that they drive.</li>
<li><strong>Move to server side testing:</strong> Lastly–and ideally–we can recommend that sites replace their client-side content tools with those that work on the server-side, so the hard work can take place before the content is delivered to the browser. Many personalization and A/B test toolkits offer server-side alternatives to their client-side tools, and we should push site owners to consider these tools instead.</li>
</ul>
<p>In an upcoming followup post, I will detail an approach we’ve been experimenting with recently that gives us the benefits of a server approach with the familiarity and flexibility of doing it with JavaScript. Stay tuned!</p>
Inlining or Caching? Both Please!2018-11-12T19:00:00-05:00<h1>Inlining or Caching? Both Please!</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> 11/13/2018</p>
<p>Last week, I was finishing a section of my slides for a presentation at the <a href="https://perfnow.nl/">Performance.Now() conference</a>. In it, I was exploring patterns that enable the browser to render a page as fast as possible by including code alongside the initial HTML so that the browser has everything it needs to start rendering the page, without making additional requests.</p>
<p>Our two go-to options to achieve this goal are inlining and server push (<a href="/lab/modernizing-delivery.html">more on how we use those</a>), but each has drawbacks: inlining prevents a file from being cached for reuse, and server push is still a bit experimental, with <a href="https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/">some browser bugs</a> still being worked out. As I was preparing to describe these caveats, I thought, “I wonder if the new Service Worker and Caching APIs could enable caching for inline code.”</p>
<p>As it turns out, they can!</p>
<h2 id="caching-an-inlined-file">Caching an Inlined File <a class="direct-link" href="#caching-an-inlined-file" aria-hidden="true">#</a></h2>
<p>Let’s start with an example. The following code snippet has a <code>style</code> element containing some inline CSS. I’ve added an ID attribute onto the <code>style</code> element so that it’s easy to find it using JavaScript. After the <code>style</code> element, a small piece of JavaScript finds that CSS and uses the new Cache API to store it in a local browser cache (with a content type of text/css) for use on subsequent pages.</p>
<pre><code>
&lt;style id="css">
.header { background: #09878}
h1 { font-size: 1.2em; col… }
h2 { margin: 0; }
…
&lt;/style>
&lt;script>
if( "caches" in window ){
var css = document.getElementById("css").innerHTML;
if( caches ){
caches.open('static').then(function(cache) {
cache.put("site.css", new Response( css,
{headers: {'Content-Type': 'text/css'}}
));
});
}
}
&lt;/script>
</code></pre>
<p>After that runs, the inline CSS will be stored in a file called <code>site.css</code> in a local service worker cache. But in order to use that file on subsequent pages, we will need to go a little further.</p>
<h3 id="rounding-this-out-with-a-worker">Rounding this out with a Worker <a class="direct-link" href="#rounding-this-out-with-a-worker" aria-hidden="true">#</a></h3>
<p>You might recognize the <code>caches</code> API above if you’ve worked with Service Workers. Despite its ties to Service Workers however, Caches is actually available in the <code>window</code> global namespace as well, so unlike workers, we are able to use Caches while having access to content from the DOM. That said, in order to ensure a subsequent page on the site will get this local <code>site.css</code> file if the browser requests it, a service worker will still be needed.</p>
<p>Here’s a common Service Worker snippet you might already have running on your site: when a file is requested by the browser, the worker looks in local caches before going to the network. With this worker running, any future requests to site.css will find it in local cache and avoid a network request.</p>
<pre><code>
(function(){
'use strict';
self.addEventListener('fetch', event => {
let request = event.request;
let url = new URL(request.url);
// Ignore non-GET requests
if (request.method === 'GET') {
// try local cache first, then go to network
event.respondWith(
caches
.match(request)
.then(response => { return response || fetch(request); })
);
}
});
})();
</code></pre>
<p>We’re now ready to handle requests to site.css on subsequent pages of the site.</p>
<p>It’s worth noting here that in order to truly take advantage of this pattern in a site’s templates, subsequent pages will need to avoid inlining the files and instead reference them externally (using a stylesheet link, a script tag, etc.). To negotiate that in an HTML template, we often set a cookie on the first visit to a site. Then, on subsequent page views, the server can check for that cookie to infer that it’s safe to assume the browser has files already in cache. If so, it can reference the files externally instead of inlining them.</p>
<p>If this process is new to you, I’d recommend reading our article <a href="/lab/modernizing-delivery.html">Modernizing our Progressive Enhancement Delivery</a>.</p>
<h2 id="a-proof-of-concept-demo">A Proof of Concept Demo <a class="direct-link" href="#a-proof-of-concept-demo" aria-hidden="true">#</a></h2>
<p>To demonstrate the pattern in action, I’ve created a simple demo page.</p>
<p>Here’s how it works: The page linked below includes some inline CSS and a caching function much like the example above. It also installs the service worker code above to check for files in local caches when requests are made. That first demo page contains a link to a second page which references site.css using a regular <code>&lt;link rel=&quot;stylesheet&quot; href=&quot;site.css&quot;&gt;</code> element. If <code>site.css</code> is loaded locally, which should happen in any service worker supporting browser, the second page will have a green background. If <code>site.css</code> is loaded from the server, the background will be red.</p>
<p>You can <a href="https://cache-origin-servers-workers.fgview.com/inline-cache.html">try the demo here</a></p>
<h2 id="taking-it-further">Taking it Further <a class="direct-link" href="#taking-it-further" aria-hidden="true">#</a></h2>
<p>There are some areas that come to mind immediately when thinking about how we can improve on the example above.</p>
<h3 id="optimizing-for-reuse">Optimizing for Reuse <a class="direct-link" href="#optimizing-for-reuse" aria-hidden="true">#</a></h3>
<p>One improvement we can make is to set up the script to look multiple inline <code>style</code> and <code>script</code> elements using a common selector, and apply custom attributes to these elements to declare their content-type, file path, and cache name for saving their content to cache. With those attributes in place, a script can find and cache many of these inlined files in a single sweep.</p>
<p>That might look like this:</p>
<pre><code>
&lt;style data-cache="static" data-type="text/css" data-path="/css/site.css">
body { background: green; color: #fff; }
a { color: #fff; }
&lt;/style>
&lt;script data-cache="static" data-type="text/javascript" data-path="/js/head.js">
alert( "This script ran from head.js, which was inlined and local." );
&lt;/script>
&lt;script>
if( "caches" in window ){
function cacheFile( file ){
caches.open( file.filecache ).then(function( cache ) {
cache.put( file.filepath, new Response( file.filetext,
{headers: {'Content-Type': file.filetype }}
));
});
}
var toCache = document.querySelectorAll( "[data-cache]" );
for( var i = 0; i < toCache.length; i++ ){
var elem = toCache[ i ];
cacheFile({
filepath: elem.getAttribute( "data-path" ),
filetype: elem.getAttribute( "data-type" ),
filecache: elem.getAttribute( "data-cache" ),
filetext: elem.innerHTML
});
}
}
&lt;/script>
</code></pre>
<p>Here’s <a href="https://cache-origin-servers-workers.fgview.com/inline-cache-2.html">a second demo that does just what you see above</a>, with both CSS and Javascript (click through to its second page again to see it work).</p>
<h3 id="optimizing-our-critical-css-approach">Optimizing our Critical CSS Approach <a class="direct-link" href="#optimizing-our-critical-css-approach" aria-hidden="true">#</a></h3>
<p>We might also consider ways to use this pattern to improve upon existing critical CSS patterns. For example, we often inline a page’s critical CSS, and then load the site’s full CSS at a later time. In this workflow, the full CSS file typically overlaps many of the CSS rules with the “critical” subset we already delivered. A better approach might be to load a full version of the CSS that <em>does not</em> contain the critical rules at all, and then use the Caches API to combine the critical and full CSS files to create a full site CSS entry in local cache.</p>
<h3 id="deliver-an-entire-website-via-html%3F%3F">Deliver an Entire Website via HTML?? <a class="direct-link" href="#deliver-an-entire-website-via-html%3F%3F" aria-hidden="true">#</a></h3>
<p>If we wanted, we could even try using this pattern to deliver an entire front-end codebase in one HTML response, mimicking a fancy web packaging format. Would it be a good idea? Probably not! But it would be fun to experiment.</p>
<p>For now, we’ll be brainstorming about other potential uses… and possibly updating our site! Thanks for reading.</p>
Mission Critical: optimizing CSS for CDN2018-04-15T20:00:00-04:00<h1>Mission Critical: optimizing CSS for CDN</h1>
<p>Posted by <a href="/about/#john-bender">John</a> 04/16/2018</p>
<p>Here at Filament Group, we recommend and make use of the <a href="/lab/modernizing-delivery.html">critical CSS</a> approach on many of our projects. Frequently those projects utilize a CDN for delivery, and we need to handle each particular implementation a little differently to accommodate different CDNs and their features. Here we’ll summarize that challenge and provide some direction on how to address it for a few example CDNs.</p>
<h2 id="overview">Overview <a class="direct-link" href="#overview" aria-hidden="true">#</a></h2>
<p>Critical CSS is a technique used to get the above-the-fold CSS for an HTML page
into the first 14kb window of the page request. This allows the browser to do a
meaningful render of the content faster. The full CSS, i.e. the CSS for
the whole page including anything above-the-fold, is loaded by the page later
and hopefully cached by the browser.</p>
<p>Once the full CSS has been cached by the browser it will be available locally on
all subsequent page requests. As a result it’s much better to serve a page that
references the cached file (via a <code>link</code> element) and avoid including the
duplicate above-the-fold CSS. With the full CSS cached, the extra CSS won’t
increase the speed of the first render and constitutes a waste of transfered
bytes, parsing, and possibly extra rendering passes.</p>
<p>This results in at least two pages for every URL. One page with the
above-the-fold and full CSS for the first request when the full CSS is not
cached, and one with just the full CSS for subsequent requests when it is
cached.</p>
<p>If you are using a CDN to cache static content, the CDN needs to cache both
pages and serve them appropriately according to the browser cache state.
Otherwise, the content of the cached page may depend on the browser cache of
the first request made to the CDN.</p>
<h2 id="edgy-content">Edgy Content <a class="direct-link" href="#edgy-content" aria-hidden="true">#</a></h2>
<p>Caching page content on the CDN edge servers has a few benefits. First and
foremost, if the edge server is geographically closer to the client than the
origin server, the CDN is likely to reduce the time it takes for the browser
get the bytes it needs to begin rendering. It also reduces load on the origin
server.</p>
<p>In our work with clients we’ve found this to be provide a big benefit to
rendering times when the testing from different locations.</p>
<!-- The downside is that, for every page variation at a URL the CDN may have to -->
<!-- cache a separate page. In the best case, Critical CSS requires at least two page -->
<!-- variants for every URL depending on the state of a particular cookie. -->
<!-- Unfortunately the number of pages cached is exponential in the number of -->
<!-- variants so this can quickly become an issue if you're already caching page -->
<!-- content with multiple variants. -->
<h2 id="conditional-page-rendering">Conditional Page Rendering <a class="direct-link" href="#conditional-page-rendering" aria-hidden="true">#</a></h2>
<p>Knowing, on the server, when the full CSS is cached by the browser is often done
via a heuristic. One approach we’ve been experimenting with lately is to use an
<code>HttpOnly</code> cookie served in the response to a request for the full
CSS file. Assuming the browser will cache the full CSS response, the server
can avoid including the above-the-fold CSS in later page requests by checking
for the cookie.</p>
<p>The following Apache configuration snippet sets the cookie based on a request
for the full CSS file.</p>
<pre><code>&lt;IfModule mod_headers.c&gt;
&lt;If &quot;%{HTTP_COOKIE} !~ /visited\=VERSION/ &amp;&amp; %{DOCUMENT_URI} =~ /full\.css/&quot;&gt;
Header set Set-Cookie &quot;visited=VERSION; Path=/; HttpOnly&quot;
&lt;/If&gt;
&lt;/IfModule&gt;
</code></pre>
<p>Note the cache breaker <code>VERSION</code> for the cookie value. When deploying a new
version of the CSS this ensures that clients with a stale cookie value will get
the critical CSS while it updates the full CSS and cookie to the new version in
the background.</p>
<p>In addition, the following snippet of code is an example of what would be
included in the head of each page. It uses Apache’s Server Side Includes to
leverage the example cookie based approach. Note that, this can be done in any
standard templating language with access to cookie state.</p>
<pre><code>
&lt;!--#if expr=&quot;$HTTP_COOKIE=/visited\=VERSION/&quot;--&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/path/to/full.css?VERSION&quot;&gt;
&lt;!--#else --&gt;
&lt;style&gt;
/* inlined critical css */
&lt;/style&gt;
&lt;!-- preload full css and apply on load.
if unsupported, rel=preload links with as=style are polyfilled via js --&gt;
&lt;link rel=&quot;preload&quot;
href=&quot;/path/to/full.css?VERSION&quot;
as=&quot;style&quot;
onload=&quot;this.onload=null;this.rel='stylesheet'&quot;/&gt;
&lt;noscript&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/path/to/full.css?VERSION&quot;&gt;
&lt;/noscript&gt;
&lt;!--#endif --&gt;
</code></pre>
<p>Together, these bits of code will produce two pages depending on the state of
the <code>visited</code> cookie and any CDN caching the content must cache and respond with
both pages according to the <code>visited</code> cookie.</p>
<h2 id="cdn-specific-configuration">CDN Specific Configuration <a class="direct-link" href="#cdn-specific-configuration" aria-hidden="true">#</a></h2>
<p>On the CDN we need the edge servers to recoginize that the pages vary based on
the <code>visited</code> cookie value and cache both versions accordingly. Below we have
collected the basics of how to do this for Akamai, CloudFront, and Fastly.</p>
<h3 id="akamai">Akamai <a class="direct-link" href="#akamai" aria-hidden="true">#</a></h3>
<p>In Akamai’s web interface the cache key can be modified by customizing a
particular “property”. In the Property Manager Editor, either in a default rule
or some other qualified rule that matches the content to be cached add the
following “Cache ID Modification” behavior:</p>
<p><img src="/images/akamai-cookie-config.png" alt="akamai config"></p>
<p>Note that the cookie is “Not Required for Caching” because we want the initial
requests that will not have the cookie to be cached.</p>
<h3 id="cloudfront-(aws)">CloudFront (AWS) <a class="direct-link" href="#cloudfront-(aws)" aria-hidden="true">#</a></h3>
<p>Cloudfront can be customized to forward a whitelist of cookies to the server and
cache based on each of the cookie values. After selecting a distribution, edit one of the
behaviors for the distribution, and change the “Forward Cookies” to <code>Whitelist</code> and add the
<code>visited</code> cookie.</p>
<p><img src="/images/cloudfront-cookie-config.png" alt="cloudfront config"></p>
<p>It’s important to note that, according to the
<a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Cookies.html">documentation</a>,
CloudFront cannot be configured to modify the cache key based on some subset of
those cookies. As a result, if you are using other cookies that need to be
forwarded to the origin server from the CDN then you may have more pages cached
based on the combinations of cookie values even if you don’t vary the page based
on the other cookies.</p>
<h3 id="fastly">Fastly <a class="direct-link" href="#fastly" aria-hidden="true">#</a></h3>
<p>Fastly uses the Varnish Configuration Language to customize the behavior of edge
servers. You can use the following snippet to modify the cache key to account
for the <code>visited</code> cookie. It uses a regular expression match and appends the
result to the cache key.</p>
<pre><code>sub vcl_hash {
set req.hash += req.url;
set req.hash += req.http.host;
set req.hash += regsub(req.http.cookie, &quot;.*visited=([^;]+);.*&quot;, &quot;\1&quot;);
#FASTLY hash
return (hash);
}
</code></pre>
<p>For more see their excellent VCL cache key
<a href="https://docs.fastly.com/guides/vcl/manipulating-the-cache-key">documentation</a>.</p>
Labels Required2018-03-14T20:00:00-04:00<h1>Labels Required</h1>
<p>Posted by <a href="/about/#maggie-wachs">Maggie</a> 03/15/2018</p>
<p>Late last year we completed an accessibility audit with a client, during which we were reminded that form fields must have properly associated <code>label</code> elements to be universally understood by screen readers and conventional browsers. We generally use <code>label</code> to mark up form fields, but occassionally use a few other valid ways to identify a field — ways that we felt confident were screen reader friendly, like <code>aria-label</code>. So why should we use a <code>label</code> in all cases? I decided to dig into why and how we should be using <code>label</code> instead of relying on elements or attributes with similar, but limited, qualities.</p>
<p>(If you need a refresher on <code>label</code> markup, MDN has a good <a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms/How_to_structure_an_HTML_form">form structure tutorial</a>.)</p>
<h2 id="what%E2%80%99s-so-special-about-the-label%3F">What’s so special about the <code>label</code>? <a class="direct-link" href="#what%E2%80%99s-so-special-about-the-label%3F" aria-hidden="true">#</a></h2>
<p>Using the <code>label</code> element checks all of the boxes for inclusivity when properly formatted: it’s directly associated with a form element (either with the <code>for</code> attribute or by nesting the element inside the <code>label</code>), displayed as text by standard browsers, and spoken by screen readers. Clicking or tapping on a label will focus the associated element (or select the <code>checkbox</code> or <code>radio</code> option), and when you focus directly on the element, screen readers will read the label. Ensuring that both visual and auditory cues are present and intelligible are critical when you consider that in WebAIM’s annual <a href="https://webaim.org/projects/screenreadersurvey7/#usage">screen reader study</a> screen reader users report relying on a mix of both, with the majority using audio exclusively.</p>
<p>It’s possible to use markup other than <code>label</code>, but when you do, users may see or hear blank form elements and have no idea what to enter. To illustrate, I tested the following inputs — one using a <code>div</code>, one with a <code>label</code> — in 3 screen readers, all with default settings: NVDA 2018 (Windows), VoiceOver (Mac), and VoiceOver (iOS). I kept the style and markup simple, for science:</p>
<figure>
<img src="/images/a11y-forms/input-email-double.png" alt="">
<figcaption>In our test page, they're visually identical</figcaption>
</figure>
<pre><code>&lt;div>Email &lt;input class="form-control" type="email">&lt;/div>
&lt;label>Email &lt;input class="form-control" type="email">&lt;/label></code></pre>
<p>VoiceOver reads both examples the same way: “Email, edit text” (Mac) or “Email, text field” (iOS). Despite that <code>div</code> has no meaningful semantics, VoiceOver interprets the “Email” text as a label.</p>
<p>NVDA, on the other hand, treats them differently. It ignores the <code>div</code> example’s text and reads “edit, blank”; when it encounters the input with <code>label</code>, it reads “Email, edit, blank”. (It’s probably worth noting that NVDA is ranked by WebAIM’s survey as the <a href="https://webaim.org/projects/screenreadersurvey7/#primary">second most popular screen reader</a>; VoiceOver is a distant third.)</p>
<p>During this test I also learned:</p>
<ul>
<li>None of these screen readers identify the specified “email” type value. They translate HTML5 input types like “email” and “search” into “text”; any numeric or date inputs are read as “stepper” (VoiceOver) or “spin button” (NVDA). One exception: VoiceOver on iOS will identify a “search field”, but no other types. Generally, we can’t rely on the <code>type</code> attribute to inform screen readers, just as we can’t rely on it to render uniformly across conventional browsers.</li>
<li>When <code>label</code> elements are used without text, some screen readers can <a href="https://www.powermapper.com/tests/screen-readers/labelling/input-text-implicit-label-blank/">identify the wrong element</a>, so proper formatting with text is key.</li>
</ul>
<p>But there are cases where we want to hide label text so that more form elements fit on the screen, or when we want to avoid redundant field identifiers. It’s fairly common to see <code>placeholder</code> or <code>aria-label</code> attributes used as de facto labels when real estate is scarce and hiding extra text from view makes sense (see: most search fields and login forms). Let’s see how they stack up.</p>
<h2 id="the-placeholder-attribute">The placeholder attribute <a class="direct-link" href="#the-placeholder-attribute" aria-hidden="true">#</a></h2>
<p>At first glance, <code>placeholder</code> does a very similar job to <code>label</code>: it’s displayed as text (within text inputs, specifically) and read aloud by screen readers. As an attribute of the text <code>input</code>, there’s no mistaking which field it belongs to.</p>
<figure>
<img src="/images/a11y-forms/placeholder.png" alt="">
<figcaption>Formatting hints help reduce errors.</figcaption>
</figure>
<pre><code>&lt;label&gt;
&lt;span class=&quot;label-text&quot;&gt;PIN&lt;/span&gt;
&lt;input class=&quot;form-control&quot; type=&quot;text&quot; placeholder=&quot;4-digit number&quot;&gt;
&lt;/label&gt;
</code></pre>
<p>The <code>placeholder</code> has a purpose, though, that diverges from that of a <code>label.</code> It’s meant to be a suggestion, formatting guideline, or hint that is ultimately replaced by user input. So while a <code>placeholder</code> may seem like a worthy label alternate on the first pass through a form, once a data value is entered, that label is gone. And that’s just one of <a href="https://medium.com/simple-human/10-reasons-why-placeholders-are-problematic-f8079412b960">several reasons</a> why <code>placeholder</code> is an inadequate substitute label. It’s much more valuable when used as a guide.</p>
<h2 id="the-aria-label-attribute">The aria-label attribute <a class="direct-link" href="#the-aria-label-attribute" aria-hidden="true">#</a></h2>
<p>The <code>aria-label</code> attribute provides label text to screen readers for identifying <a href="https://developer.paciellogroup.com/blog/2017/07/short-note-on-aria-label-aria-labelledby-and-aria-describedby/">interactive elements, or elements with ARIA roles</a>, and it produces the same audio feedback as the <code>label</code> element. There’s no reason to use both on the same form field, and we actually recommend against trying. Out of curiousity, I tested both on the same field and each screen reader read the combination of <code>label</code>, <code>aria-label</code>, and <code>placeholder</code> text differently: NVDA omits the <code>placeholder</code>, VoiceOver (Mac) omits the <code>label</code> text, and VoiceOver (iOS) omits the <code>aria-label</code>. It’s best to choose a single labelling method.</p>
<p>To that end, consider that <code>aria-label</code> is limited in 2 important ways when it’s used in place of a standard <code>label</code>:</p>
<ul>
<li>it doesn’t display any text, so you’d need to use a separate text element for sighted users. I’ve seen <code>placeholder</code> text or default values used with <code>aria-label</code> to provide the visual piece, but as noted earlier, these values are intended to change with user input.</li>
<li>more imporantly, the standard implementation of ARIA attributes continues to be a work in progress for both <a href="https://caniuse.com/#search=aria">conventional browsers</a> and <a href="https://www.powermapper.com/tests/screen-readers/labelling/input-text-aria-label/">screen readers</a>.</li>
</ul>
<p>Given what <code>label</code> can do, relying on <code>aria-label</code> plus text seems like a long way around the barn (looking at you, Google apps), and one that may not pass the inclusivity test for reaching the widest range of users. Like <code>placeholder</code>, <code>aria-label</code> doesn’t quite stand up to the robustness of <code>label</code> markup, so we stick to using <code>label</code> exclusively. But it can be very useful in other ways, like <a href="https://meyerweb.com/eric/thoughts/2018/01/18/headings-and-labels/">clarifying duplicate ARIA roles</a>.</p>
<p>The problem these attributes are often used to solve — saving space — can also be accomplished using <code>label</code> markup. You can easily hide labels and still ensure your form is accessible by using styles that keep them available to screen readers.</p>
<h2 id="know-when-to-(not)-show-'em-(labels)">Know when to (not) show 'em (labels) <a class="direct-link" href="#know-when-to-(not)-show-'em-(labels)" aria-hidden="true">#</a></h2>
<p>When we want to hide markup but ensure that screen readers will find it, we use a class called <code>a11y-only</code>. In an earlier post we covered <a href="/lab/accessible-responsive.html#hiding">how to hide elements in an accessible way</a>; if you’re not familiar, I recommend reading that first.</p>
<p>The basic idea is to mark up a form element as you normally would, then enclose the <code>label</code> text in an element assigned the <code>a11y-only</code> class, which stashes it away for screen readers. Once hidden, we just need to make sure the element maintains its affordance for sighted users, and we can do that with helpful attributes or icons.</p>
<h3 id="use-multiple-visual-cues">Use multiple visual cues <a class="direct-link" href="#use-multiple-visual-cues" aria-hidden="true">#</a></h3>
<p>We often see a global search input in page headers and navigation bars where space is limited:</p>
<figure>
<img src="/images/a11y-forms/search-label.png" alt="">
<figcaption>Is the label necessary?</figcaption>
</figure>
<pre><code>&lt;label class=&quot;form-search&quot;&gt;
&lt;span class=&quot;label-text&quot;&gt;Search&lt;/span&gt;
&lt;input class=&quot;form-control&quot; type=&quot;search&quot; placeholder=&quot;Enter a term&quot;&gt;
&lt;/label&gt;
&lt;button class=&quot;form-control&quot;&gt;Go&lt;/button&gt;
</code></pre>
<p>Using a search icon and <code>placeholder</code> attribute (hint: “Enter a term”) makes the visible label somewhat redundant, so we can safely hide it from view with our <code>a11y-only</code> class to shrink the footprint:</p>
<figure>
<img src="/images/a11y-forms/search-no-label.png" alt="">
<figcaption>Still clearly a search field</figcaption>
</figure>
<pre><code>&lt;label class=&quot;form-search&quot;&gt;
&lt;span class=&quot;label-text a11y-only&quot;&gt;Search&lt;/span&gt;
&lt;input class=&quot;form-control&quot; type=&quot;search&quot; placeholder=&quot;Enter a term&quot;&gt;
&lt;/label&gt;
&lt;button class=&quot;form-control&quot;&gt;Go&lt;/button&gt;
</code></pre>
<p>Visible and accessibly hidden labels are read the same way by screen readers. In both cases you’ll hear:</p>
<ul>
<li><strong>NVDA:</strong> Search, Enter a term, edit, blank</li>
<li><strong>VO Mac:</strong> Search, edit text, Enter a term</li>
<li><strong>VO iOS:</strong> Search, Enter a term, search field, double tap to edit</li>
</ul>
<p><em>Aside: By default, NVDA will speak “autocomplete” when identifying the field unless it’s explicitly turned off. Use the <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion"><code>autocomplete</code> attribute</a> to control this feature.</em></p>
<h3 id="consolidate-labels-into-a-single-heading">Consolidate labels into a single heading <a class="direct-link" href="#consolidate-labels-into-a-single-heading" aria-hidden="true">#</a></h3>
<p>Sometimes we’ll use 2-3 fields to collect a set of related data, in which case visible label text isn’t necessary for each individual field. Instead we can use a single heading for the set and accessibly hide individual labels from view, just like we did in the previous example. The container markup we use to group these fields can also convey meaning to screen readers.</p>
<figure>
<img src="/images/a11y-forms/group.png" alt="">
<figcaption>One label (heading) is sufficient</figcaption>
</figure>
<p>When you use <code>fieldset</code> and <code>legend</code>, NVDA and VoiceOver (iOS only) read the <code>legend</code> before the first field’s label. So the following markup:</p>
<figure>
<img src="/images/a11y-forms/group-fieldset.png" alt="">
</figure>
<pre><code>&lt;fieldset&gt;
&lt;legend&gt;Birthday&lt;/legend&gt;
&lt;label&gt;&lt;span class=&quot;a11y-only&quot;&gt;Day&lt;/span&gt;
&lt;select class=&quot;form-control&quot;&gt;
&lt;option disabled selected&gt;Day&lt;/option&gt;
&lt;option&gt;1&lt;/option&gt;
...
&lt;option&gt;31&lt;/option&gt;
&lt;/select&gt;
&lt;/label&gt;
&lt;label&gt;&lt;span class=&quot;a11y-only&quot;&gt;Month&lt;/span&gt;
&lt;select class=&quot;form-control&quot;&gt;
&lt;option aria-hidden=&quot;true&quot; aria-role=&quot;presentation&quot;&gt;Month&lt;/option&gt;
&lt;option&gt;January&lt;/option&gt;
...
&lt;option&gt;December&lt;/option&gt;
&lt;/select&gt;
&lt;/label&gt;
&lt;/fieldset&gt;
</code></pre>
<p>Is read aloud as:</p>
<ul>
<li><strong>NVDA:</strong> Birthday grouping; Day, combobox, [value], collapsed</li>
<li><strong>VO iOS:</strong> Birthday, form start; Day, [value], popup button, double tap to activate the picker</li>
</ul>
<p>(VoiceOver on Mac reads the <code>legend</code> as a typical text node.)</p>
<p>Alternately, you could use a <code>div</code> with a heading element to group related form elements, but there are pros and cons that you’d need to weigh against your goals. With a <code>div</code> + <code>heading</code> you’d lose the bonus “grouping” and “form start” language that screen readers speak when they encounter a <code>fieldset</code> + <code>legend</code>, but when you consider that <a href="https://webaim.org/projects/screenreadersurvey7/#finding">most screen reader users rely on headings for navigation</a>, using a <code>div</code> + <code>heading</code> may be more helpful structural markup for a multi-part form.</p>
<h2 id="conclusion%3A-label-everything">Conclusion: <code>label</code> everything <a class="direct-link" href="#conclusion%3A-label-everything" aria-hidden="true">#</a></h2>
<p>Out of the box, the <code>label</code> element is all we need to indentify fields in an accessible way. Used in combination with accessible hiding and other helper elements, like <code>placeholder</code> or icons, we can ensure that a form element’s purpose is clearly communicated to all users.</p>
<p>The code that I tested is available on <a href="/examples/a11y-forms/">this demo page</a>, and I’ve included markup variations so you can see and hear for yourself how they differ across browsers and screen readers. And if you missed the first post in this (very informal) series, check out <a href="/lab/a11y-links.html">Accessible Links Re:visited</a>.</p>
<p>Want to chat about this post? Find us at <a href="https://twitter.com/filamentgroup">@filamentgroup</a> on Twitter.</p>
Swapping Images with the Sizes Attribute2018-03-08T19:00:00-05:00<h1>Swapping Images with the Sizes Attribute</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> 3/9/2018</p>
<p>Over the years we’ve designed components that enable users to magnify, swipe, and pan around images in a wide variety of ways. Early on, it was tricky to build these components in ways that used image sizes responsibly, and we’d sometimes end up delivering larger images than we’d prefer–ensuring that the image would at least be sharp across all devices. Thankfully, as “responsive” image standards have gained support we’ve been able to offload much of that source selection logic to the browser, and in that regard, one particular trick has served us really well that we wanted to share.</p>
<h2 id="recapping-srcset-and-sizes">Recapping srcset and sizes <a class="direct-link" href="#recapping-srcset-and-sizes" aria-hidden="true">#</a></h2>
<p>If you’ve worked with the <code>srcset</code> attribute on images, you’re probably familiar with its relationship to the <code>sizes</code> attribute. For sake of context here’s a quick refresher: the <code>srcset</code> attribute offers a place to list one more more <em>potential</em> source files (paired with their inherent pixel dimensions) for an image, and the <code>sizes</code> attribute gives the browser the rough idea of the dimensions at which the image will be displayed in the page layout. Using these two attributes, the browser can decide which source file should be loaded and displayed at a given viewport size.</p>
<p>Here’s an example of an image that uses srcset and sizes:</p>
<pre><code>&lt;img src=&quot;imgs/bike.jpg&quot;
srcset=&quot;imgs/bike.jpg 480w, imgs/bike-1200.jpg 1200w, imgs/bike-2000.jpg 2000w&quot;
sizes=&quot;100vw&quot;
alt=&quot;A red bicycle&quot;&gt;
</code></pre>
<p>Above we have 3 potential sizes of the same image at 480px wide, 1200px wide, and 2000px wide (note the <code>w</code> unit representing width). The <code>sizes</code> attribute says the image will be displayed at <code>100vw</code> (aka 100% of the viewport width), which happens to be the default value for <code>sizes</code> if it was omitted. With this information, the browser will choose a source based on relevant conditions it already knows, like the browser’s viewport size and the device’s screen resolution (which may act as a multiplier of the viewport size when selecting an image), and even connection speed, typically erring on the side of using an image that is too large over one that isn’t large enough.</p>
<p><code>sizes</code> accepts more complex conditions too: it allows us to specify different image display dimensions depending on media queries. For example, an image can be described as <code>100vw</code> wide when the viewport is smaller than say, <code>40em</code>, and then only 600px wide at viewport sizes larger than <code>40em</code>:</p>
<pre><code>&lt;img src=&quot;imgs/bike.jpg&quot;
srcset=&quot;imgs/bike.jpg 480w, imgs/bike-1200.jpg 1200w, imgs/bike-2000.jpg 2000w&quot;
sizes=&quot;(min-width:40em) 600px, 100vw&quot;
alt=&quot;A red bicycle&quot;&gt;
</code></pre>
<p>…and naturally, any number of those conditions can be used to describe more complex layout scenarios.</p>
<h2 id="letting-sizes-do-more-work">Letting Sizes Do More Work <a class="direct-link" href="#letting-sizes-do-more-work" aria-hidden="true">#</a></h2>
<p>One thing that is interesting about <code>sizes</code> is that the browser trusts its description over the actual image’s size in the page layout, which may not actually correspond at all. In other words, <code>sizes</code> allows you to lie about an image’s layout size, and that turns out to be pretty powerful for dynamic image components like a magnifier tool.</p>
<p>Take the first example again: the <code>sizes</code> attribute is set to <code>100vw</code>, so if the browser viewport is say, 400px wide, the browser will try to select the smallest available image that is at least 400px wide (imgs/bike.jpg in this case).</p>
<pre><code>&lt;img src=&quot;imgs/bike.jpg&quot;
srcset=&quot;imgs/bike.jpg 480w, imgs/bike-1200.jpg 1200w, imgs/bike-2000.jpg 2000w&quot;
sizes=&quot;100vw&quot;
alt=&quot;A red bicycle&quot;&gt;
</code></pre>
<p>But! Given the same viewport conditions, if we were to change the <code>sizes</code> attribute value to <code>200vw</code>, the browser will select a source that’s appropriate to use for an image that’s 200% of the viewport width, or 800px wide (400 x 2). The 1200px wide image is closest to that value so it’ll use that file instead, even if the image is actually styled at a smaller size in the layout (with CSS).</p>
<pre><code>&lt;img src=&quot;imgs/bike.jpg&quot;
srcset=&quot;imgs/bike.jpg 480w, imgs/bike-1200.jpg 1200w, imgs/bike-2000.jpg 2000w&quot;
sizes=&quot;200vw&quot;
alt=&quot;A red bicycle&quot;&gt;
</code></pre>
<p>It’s worth mentioning here too that the browser will continuously evaluate an image’s source selection throughout the browsing session, since it may need to use a different source if the user resizes their browser, drags the browser window to a higher resolution display, or changes their device orientation (which often resizes the viewport). Also, it will re-evaluate whenever the <code>sizes</code> attribute’s value changes, which means we can use JavaScript to do that in real time if the user interface calls for it.</p>
<p>Knowing that, you might see how we could manipulate <code>sizes</code> to swap sources in a magnifier component…</p>
<h2 id="a-quick-demo">A Quick Demo <a class="direct-link" href="#a-quick-demo" aria-hidden="true">#</a></h2>
<p>We put together some quick demos of a JavaScript-enhanced image magnifier component to show a practical use of this behavior in action. In this first demo, the magnification happens “inline”, meaning it zooms the image in-place with a higher resolution version, and it overflows its boundaries in the layout so the user can pan around. The panning behavior works in one of two modes: immediately on mousemove or touch-drag, or with regular overflow scrolling, if you click the zoom button first (which makes keyboard use possible).</p>
<p>In either behavior, the JavaScript swaps the source for a higher resolution image by merely toggling the existing image’s <code>sizes</code> attribute to a desired multiple of the image’s current width in the page. For example, if the desired image magnification is 5x, and the image is 200px wide when the user attempts to magnify it, the script will toggle the image’s <code>sizes</code> attribute value to <code>sizes=&quot;1000px&quot;</code>, and the browser will find a new appropriate source for the image among the list of <code>srcset</code> sources that was already in the HTML when it first arrived.</p>
<link rel="stylesheet" href="https://filamentgroup.github.io/enlarge/src/enlarge.css">
<div class="enlarge_pane_contain" style="max-width: 500px;">
<div class="enlarge_pane">
<div class="enlarge inline-demo">
<div class="enlarge_contain">
<img src="https://filamentgroup.github.io/enlarge/docs/imgs/bike.jpg" srcset="https://filamentgroup.github.io/enlarge/docs/imgs/bike.jpg 480w, https://filamentgroup.github.io/enlarge/docs/imgs/bike-1200.jpg 1200w, https://filamentgroup.github.io/enlarge/docs/imgs/bike-2000.jpg 2000w" sizes="100vw" alt="" id="test-img">
</div>
<a href="https://filamentgroup.github.io/enlarge/docs/imgs/bike-2000.jpg" class="enlarge_btn" title="Toggle Zoom">Toggle Zoom</a>
</div>
</div>
</div>
<p>After zooming out, the JavaScript returns the <code>sizes</code> attribute to its former value. This may trigger the browser to display the original low resolution image again, but it may just keep the higher quality one in place instead–potentially avoiding a request to a lower quality image that isn’t as good as the one already in the page.</p>
<p>For the sake of demonstration, the inline behavior above is just one way to utilize this pattern. Here’s a second demo that creates a “loupe” magnifier on mouse hover. In this case, the JavaScript duplicates the original image element to place it in another overlaid element, and it sets that new image’s <code>sizes</code> attribute to a multiplied value to get a higher resolution source:</p>
<div class="enlarge_pane_contain" style="max-width: 500px;">
<div class="enlarge_pane">
<div class="enlarge">
<div class="enlarge_contain">
<img src="https://filamentgroup.github.io/enlarge/docs/imgs/bike.jpg" srcset="https://filamentgroup.github.io/enlarge/docs/imgs/bike.jpg 480w, https://filamentgroup.github.io/enlarge/docs/imgs/bike-1200.jpg 1200w, https://filamentgroup.github.io/enlarge/docs/imgs/bike-2000.jpg 2000w" sizes="100vw" alt="" id="test-img">
</div>
<a href="https://filamentgroup.github.io/enlarge/docs/imgs/bike-2000.jpg" class="enlarge_btn" title="Toggle Zoom" style="display: none;">Toggle Zoom</a>
</div>
</div>
</div>
<p>Overall, we find this trick to be really handy, particularly as it allows our clients to place all of their image sizes in one place in the markup, which tends to make integration easier with various content management systems.</p>
<p>So that’s one thing you can do to simplify pretty complex image selection with the new <code>sizes</code> attribute. In the near future, we’ll likely clean up the code for this demo component and release it in a reusable way, (<em>though, <a href="https://github.com/filamentgroup/enlarge/tree/updates">the messy source code</a> is open if you want to poke around</em>).</p>
<p>Thanks for reading!</p>
<script src="https://filamentgroup.github.io/enlarge/libs/jquery/jquery.js"></script>
<script src="https://filamentgroup.github.io/enlarge/src/enlarge.js"></script>
<script src="https://filamentgroup.github.io/enlarge/src/enlarge.init.js"></script>
<script src="https://filamentgroup.github.io/enlarge/docs/init.js"></script>
It’s Dangerous to Go Stallone. Take Glyphhanger2018-03-04T19:00:00-05:00<h1>It’s Dangerous to Go Stallone. Take Glyphhanger</h1>
<p>Posted by <a href="/about/#zach-leatherman">Zach</a>, title by <a href="/about/#scott-jehl">Scott</a> 3/5/2018</p>
<p>Psst.</p>
<p>Hey, you.</p>
<p>Yeah, you.</p>
<p>I’m gonna tell you a secret. A well guarded and little known secret—whispered about in hallway tracks of web performance conferences around the world. A web font performance trick that will take your font loading to the next level.</p>
<p>Here it is—are you ready? <em>Smaller web fonts load faster.</em> I know, I know. Mind blowing stuff.</p>
<p>Okay, fine–I’ll concede. This isn’t as groundbreaking or as secretive as I originally claimed. But it is helpful!</p>
<p>Practically speaking, making web fonts as small as possible is not always straightforward. The most straightforward optimization is to use compressed web font files, specifically the WOFF2 format but also its predecessor WOFF for compatibility.</p>
<p>A less well known, but very useful option—and the one I’d like to discuss in-depth today—is removing potentially unnecessary characters from a font file. This is a process called <em>subsetting</em>.</p>
<h2 id="subsetting">Subsetting <a class="direct-link" href="#subsetting" aria-hidden="true">#</a></h2>
<p>Let’s start with the most popular font on Google Fonts, Roboto. For experiment’s sake (although you can download it from Google Fonts) let’s download an unoptimized copy straight from the source, the <a href="https://github.com/google/roboto/releases">Roboto GitHub repository</a>.</p>
<p>Opening up the <code>roboto-android.zip</code> file, we can find <code>Roboto-Regular.ttf</code>—a completely unoptimized desktop font version of Roboto. It weighs in at <code>306 KB</code> and includes 3,359 distinct glyphs (according to <a href="https://fontdrop.info/">Font Drop</a>). That’s a hefty font. Not a hefty weight—we’re not using the bold version of Roboto—I only mean that the file size is large. Was that clear? Hopefully. I think you get what I mean.</p>
<p>Font Drop also reports that Roboto contains support for over 40 languages:</p>
<blockquote>
<p>Afrikaans Albanian Azerbaijani Belarusian Bosnian Bulgarian Catalan Croatian Czech Danish Dutch English Estonian Finnish French German Greek Hungarian Icelandic Italian Latvian Lithuanian Macedonian Maltese Norwegian Ossetic Polish Portugese Romanian Russian Serbian Slovak Slovenian Spanisch Swedish Turkish Ukrainian Uzbek Vietnamese Zulu</p>
</blockquote>
<p>Does your content necessitate characters for those languages? Or are those characters unnecessary and unlikely to appear on your site? This isn’t a hypothetical question—let’s answer it!</p>
<h2 id="use-glyphhanger">Use glyphhanger <a class="direct-link" href="#use-glyphhanger" aria-hidden="true">#</a></h2>
<p>We can test our site to see what glyphs are used at a URL using a tool we’ve developed called <code>glyphhanger</code>.</p>
<p>Find <code>glyphhanger</code> on <a href="https://github.com/filamentgroup/glyphhanger"><strong>GitHub</strong></a> or <a href="https://www.npmjs.com/package/glyphhanger"><strong>npm</strong></a>.</p>
<pre><code>npm install -g glyphhanger
</code></pre>
<p>At its simplest, glyphhanger can take a URL and return a Unicode range for all of the content used on the site.</p>
<p>For example (don’t include the <code>$</code> if you copy these commands):</p>
<pre><code>$ glyphhanger https://www.filamentgroup.com/
U+20,U+21,U+26,U+28,U+29,U+2B-38,U+3A,U+3F-47,U+49,U+4C-50,U+52-55,U+57-5A,U+61-70,U+72-77,U+79,U+7C,U+A9,U+DC,U+2014,U+2019,U+25A2
</code></pre>
<p>Let’s take <code>Roboto-Regular.ttf</code> and subset it this Unicode range. glyphhanger can help with this too:</p>
<p><em>Subsetting functionality in glyphhanger requires you to install <a href="https://github.com/fonttools/fonttools">fonttools</a>, a python project. The glyphhanger README includes <a href="https://github.com/filamentgroup/glyphhanger#installing-pyftsubset">installation instructions for Mac OS X</a>.</em></p>
<pre><code>$ glyphhanger https://www.filamentgroup.com/ --subset=Roboto-Regular.ttf --formats=woff2,woff
Subsetting Roboto-Regular.ttf to Roboto-Regular-subset.woff (was 298.45 KB, now 14.09 KB)
Subsetting Roboto-Regular.ttf to Roboto-Regular-subset.woff2 (was 298.45 KB, now 10.63 KB)
</code></pre>
<p>Note that in the above example, <code>--formats=woff2,woff</code> means to output both a WOFF and WOFF2 subset file. Valid options include: <code>woff2</code>, <code>woff</code>, <code>woff-zopfli</code> (for better WOFF compression compared to <code>woff</code>), and <code>ttf</code>.</p>
<h3 id="make-the-files-usable-site-wide">Make the files usable site-wide <a class="direct-link" href="#make-the-files-usable-site-wide" aria-hidden="true">#</a></h3>
<p>This works great for a single page on our site. But to be fair, we want to use one set of web font files that works with all of the pages on our site—not just for the home page. There are a couple of ways to make this work:</p>
<ol>
<li>Pass in multiple URLs</li>
<li>Use the glyphhanger Spider to find URLs for you</li>
<li>Pass in a white-list of characters or a <code>unicode-range</code></li>
</ol>
<p>Let’s look at a few examples:</p>
<h4>Pass in multiple URLs</h4>
<pre><code>$ glyphhanger https://www.filamentgroup.com/ https://www.filamentgroup.com/code/
</code></pre>
<h4>Use the Spider to find URLs for you</h4>
<p>Use the <code>--spider</code> option to find all of the <code>a[href]</code> attribute values that contain local URLs and spider those pages as well. This will report the union of all of the Unicode ranges.</p>
<pre><code>$ glyphhanger https://www.filamentgroup.com/ --spider --spider-limit=10
</code></pre>
<p>Configure the maximum limit with <code>--spider-limit</code> (default is 10).</p>
<h4>Pass in a whitelist</h4>
<p>Use the <code>--whitelist</code> option to pass in a Unicode range or a list of characters you’d like to include by default in addition to any URLs that get interpreted. At time of writing, glyphhanger (v3.0) also includes shortcuts for <code>--US_ASCII</code> and <code>--LATIN</code>.</p>
<p>Using <code>--whitelist</code> makes the URL argument optional.</p>
<pre><code>$ glyphhanger --whitelist=&quot;ABCD&quot;
U+41-44
</code></pre>
<pre><code>$ glyphhanger --US_ASCII
U+20-7E
</code></pre>
<p>Let’s subset our Roboto font using a whitelist.</p>
<pre><code>$ glyphhanger --whitelist=&quot;U+20-7E&quot; --subset=Roboto-Regular.ttf --formats=woff2
U+20-7E
Subsetting Roboto-Regular.ttf to Roboto-Regular-subset.woff2 (was 298.45 KB, now 10.78 KB)
</code></pre>
<p>Nice, right?</p>
<h3 id="subset-to-a-specific-font-family">Subset to a specific font-family <a class="direct-link" href="#subset-to-a-specific-font-family" aria-hidden="true">#</a></h3>
<p>Let’s say, instead of Roboto, we have a specific monospace web font we’d like to use in our code blocks and syntax highlighted code on our site. The Unicode range for monospace code content may be very different from the characters used for other content on our site. glyphhanger can help here too. We can pass in a comma separated list of one or more <code>font-family</code> names to fetch only the content in nodes using those <code>font-family</code> values.</p>
<pre><code>$ glyphhanger --family='Consolas,monospace' https://www.filamentgroup.com/lab/preload-ctm.html
U+A,U+20-22,U+26,U+28,U+29,U+2B-30,U+38,U+3A-3E,U+41-43,U+45,U+4A,U+4C-4E,U+53,U+54,U+5B,U+5D,U+61-79,U+7B-7D
</code></pre>
<p>It works with <code>--subset</code> too. You can also use <code>--json</code> to see all ranges organized by <code>font-family</code>. <code>&quot;*&quot;</code> is the universal list of all characters on the page.</p>
<pre><code>$ glyphhanger https://www.filamentgroup.com/code/ --json
{&quot;*&quot;: &quot;U+20,U+21,U+26,U+28,U+29,U+2B-38,U+3A,U+3F-4A,U+4C-59,U+61-7A,U+A9,U+B0,U+2019,U+201C,U+201D&quot;,&quot;Source Sans Pro&quot;: &quot;U+20,U+21,U+26,U+28,U+29,U+2B-38,U+3A,U+3F-4A,U+4C-59,U+61-7A,U+A9,U+B0,U+2019,U+201C,U+201D&quot;,&quot;Consolas&quot;: &quot;U+20,U+28,U+29,U+3A,U+40,U+61,U+63-66,U+69,U+6B-70,U+72-75,U+78-7A&quot;}
</code></pre>
<h3 id="output-css-that-you-can-use">Output CSS that you can use <a class="direct-link" href="#output-css-that-you-can-use" aria-hidden="true">#</a></h3>
<p>glyphhanger will even make your <code>@font-face</code> blocks in CSS for you. Using <code>--css</code> will output a <code>@font-face</code> block to the console. Of course, it’ll add the <code>unicode-range</code>, but it’ll also add the right <code>src</code> with the correct file names returned from <code>--subset</code>. If you’re using <code>--family</code>, it’ll add a <code>font-family</code> value too.</p>
<pre><code>$ glyphhanger --US_ASCII --family='Source Sans Pro' --subset=Roboto-Regular.ttf --formats=woff2,woff --css
U+20-7E
Subsetting Roboto-Regular.ttf to Roboto-Regular-subset.woff (was 298.45 KB, now 14.37 KB)
Subsetting Roboto-Regular.ttf to Roboto-Regular-subset.woff2 (was 298.45 KB, now 10.78 KB)
@font-face {
font-family: Source Sans Pro;
src: url(Roboto-Regular-subset.woff2) format(&quot;woff2&quot;), url(Roboto-Regular-subset.zopfli.woff) format(&quot;woff&quot;);
unicode-range: U+20-7E;
}
</code></pre>
<h2 id="a-web-font-utility-belt">A Web Font Utility Belt <a class="direct-link" href="#a-web-font-utility-belt" aria-hidden="true">#</a></h2>
<p><code>glyphhanger</code> is a useful tool when working with web fonts—it can help optimize your font files very quickly. But don’t get too hasty. Take proper care to perform due diligence before subsetting:</p>
<ol>
<li>Make sure subsetting agrees with the licensing restrictions on the font file.</li>
<li>Don’t be too aggressive with your subset. If you’re using web fonts for user generated content or content that is likely to change in the future, your Unicode range should be broad enough to accommodate this.</li>
</ol>
<p>Download glyphhanger and give it a try! We welcome any and all feedback at our <a href="https://github.com/filamentgroup/glyphhanger/issues">Issue Tracker</a>. What else would you like to do with your web fonts?</p>
Web Fonts are ▢▢▢ Rocket Science2018-02-11T19:00:00-05:00<h1>Web Fonts are ▢▢▢ Rocket Science</h1>
<p>Posted by <a href="/about/#zach-leatherman">Zach</a> 1/5/2018</p>
<p>Web fonts can be tricky—but are they rocket science? Web browsers have decided to make them invisible while they’re loading to avoid rendering system fonts to users. In order to properly manage the performance of our web fonts, we respectfully disagree with that decision. Roll up the sleeves on your lab coat, y’all.</p>
<p>In this talk, we discuss practical ways to stabilize the four main pillars of proper font loading:</p>
<ol>
<li>Avoiding invisible text.</li>
<li>Triggering downloads sooner.</li>
<li>Grouping repaints to reduce reflow.</li>
<li>If you make font files smaller, they load faster.</li>
</ol>
<div class="rwd-embed-wrap"><iframe src="https://player.vimeo.com/video/254727749" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe></div>
<p><a href="https://vimeo.com/254727749">SmashingConf London &mdash; Zach Leatherman on &lsquo;Web Fonts are ▢▢▢ Rocket Science&rsquo;</a> from <a href="https://vimeo.com/smashingmagazine">Smashing Magazine</a> on <a href="https://vimeo.com">Vimeo</a>.</p>
<h2 id="timestamps-for-content">Timestamps for Content <a class="direct-link" href="#timestamps-for-content" aria-hidden="true">#</a></h2>
<ul>
<li><code>01:25</code> <a href="https://www.zachleat.com/web/mitt-romney-webfont-problem/">The Mitt Romney Web Font Loading Problem</a></li>
<li><code>03:08</code> The Flash of Invisible Text (FOIT) and Flash of Unstyled Text (FOUT)</li>
<li><code>07:15</code> The History of @font-face Loading Behavior</li>
<li><code>13:10</code> @font-face Syntax</li>
<li><code>15:18</code> <code>font-display</code> for FOUT</li>
<li><code>18:08</code> <code>font-synthesis</code> as a tool for better FOUT</li>
<li><code>21:11</code> Using Faux Pas to detect <code>font-synthesis</code>: <a href="https://github.com/filamentgroup/faux-pas">faux-pas</a> and <a href="https://github.com/filamentgroup/node-faux-pas">node-faux-pas</a></li>
<li><code>24:25</code> <code>preload</code></li>
<li><code>25:30</code> The FOUT with a Class font loading strategy (for broadly supported, robust font loading)</li>
<li><code>30:43</code> Critical FOFT with a Data URI</li>
<li><code>32:39</code> Subsetting web fonts, <a href="https://github.com/filamentgroup/glyphhanger">Glyphhanger</a></li>
<li><code>35:12</code> Variable Fonts</li>
</ul>
The Spectre Attack and Critical CSS2018-01-04T19:00:00-05:00<h1>The Spectre Attack and Critical CSS</h1>
<p>Posted by <a href="/about/#john-bender">John</a> 1/5/2018</p>
<h2 id="tl%3Bdr">TL;DR <a class="direct-link" href="#tl%3Bdr" aria-hidden="true">#</a></h2>
<p>Due to recently <a href="https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with-side.html">disclosed</a> security vulnerabilities for nearly all computers, you should disable any
JavaScript cookie manipulation on your website (e.g. when using the <a href="https://www.filamentgroup.com/lab/performance-rwd.html">critical
CSS technique</a>) by setting your cookies to be <a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-02#section-5.3.7">SameSite</a> and <a href="https://www.owasp.org/index.php/HttpOnly">HttpOnly</a> on the server, as
<a href="https://sites.google.com/a/chromium.org/dev/Home/chromium-security/ssca">recommended</a> on the Chromium wiki. Otherwise, sensitive data, like session keys, may be exposed to malicious third parties.</p>
<h2 id="meltdown-and-spectre-vulnerabilities">Meltdown and Spectre Vulnerabilities <a class="direct-link" href="#meltdown-and-spectre-vulnerabilities" aria-hidden="true">#</a></h2>
<p>Two days ago a group of security researchers at Google disclosed two security flaws, dubbed Meltdown and Spectre, that allow processor exploits to steal passwords and other sensitive user data from almost any device made in the past 20 years.</p>
<p>Taken together these vulnerabilities affect nearly every computer on the web. In this article, we’re focused on mitigating a variant of the Spectre attack since it has a JavaScript proof of concept exploit. In particular we are concerned with how it can be used to expose sensitive data in a site’s cookies.</p>
<p>The attack works by exposing same-process memory that would otherwise be unreadable to JavaScript. This is an issue if a browser loads two sites into the same rendering process, which can
happen in spite of <a href="https://sites.google.com/a/chromium.org/dev/developers/design-documents/process-models#Supported_Models">process-per-site-instance isolation</a> like that used by default in Chrome.</p>
<p>We haven’t yet seen a detailed description of how the page might be added to another page’s memory (see “Unknowns” below) but one way might be through a <a href="https://www.chromium.org/developers/design-documents/oop-iframes">same process iframe render</a>. The malicious site would load the target site in an iframe which would run the <code>document.cookie</code> read in the same process, exposing the cookie data to the attack.</p>
<h2 id="impact-on-web-developers">Impact on Web Developers <a class="direct-link" href="#impact-on-web-developers" aria-hidden="true">#</a></h2>
<p>These exploits should be a concern for web developers since a cookie that is not flagged as <code>SameSite</code> and <code>HttpOnly</code> on the server may be more likely to expose the cookie data to a third party.</p>
<p>Until browsers implement mitigations as defaults we recommend using those flags. This is in keeping with the <a href="https://sites.google.com/a/chromium.org/dev/Home/chromium-security/ssca">recommendations</a> on the Chromium wiki and it prevents JavaScript from accessing the cookie which should mitigate the attack.</p>
<p>It’s strongly recommended that you examine any plugins, libraries, and 3rd party tools that manipulate cookies to see if they are a potential security concern.</p>
<h2 id="spectre-and-criticalcss">Spectre and CriticalCSS <a class="direct-link" href="#spectre-and-criticalcss" aria-hidden="true">#</a></h2>
<p>We frequently use a <a href="https://www.filamentgroup.com/lab/performance-rwd.html">technique</a> which works by identifying and including the “above the fold” CSS inline in the head of the page for a “fresh” (empty cache) page load to speed up
rendering. Then, once the client is likely to have cached the full CSS, typically a cookie is set on the client so that the server can remove the inlined CSS on subsequent page requests,
reducing page weight.</p>
<p>The problem is that, in setting the cookie, the page pulls all of the cookie data into memory exposing any sensitive data, like session information, to the Spectre attack.</p>
<p>Our current advice is if you are using CriticalCSS with a cookie on your website, you should disable the <code>document.cookie</code> read by setting the <code>SameSite</code> and <code>HttpOnly</code> flags as mentioned above. Otherwise sensitive data may be exposed to malicious third parties.</p>
<p>It’s important to note the distinction between the <a href="https://github.com/filamentgroup/criticalcss">CriticalCSS</a> <em>library</em> and the critical CSS <em>technique</em>. The CriticalCSS library extracts above-the-fold CSS that should be inlined in the page. The technique of dynamically inlining CSS based on a cookie value is simply one way this could be served to the browser.</p>
<h2 id="unknowns">Unknowns <a class="direct-link" href="#unknowns" aria-hidden="true">#</a></h2>
<p>As a matter of transparency there are still many facets of these vulnerabilities which are unclear to us:</p>
<ol>
<li>
<p>How precisely can a malicious site get a target site into the render process? We speculated above on that note but it’s not documented anywhere.</p>
</li>
<li>
<p>Is the danger of loading cookies confined to the first variant of the Spectre Attack? It seems like this is almost certainly not the case, but for now it’s the only variant with a
JavaScript proof-of-concept that we are aware of.</p>
</li>
<li>
<p>Many of the patches and recommendations for users and site operators (site isolation, <code>HttpOnly</code> cookies, reduced performance sampling, etc) are labeled as mitigations and not fixes. It seems these vulnerabilities may be with us for the forseable future.</p>
</li>
</ol>
<h2 id="references">References <a class="direct-link" href="#references" aria-hidden="true">#</a></h2>
<ol>
<li><a href="https://spectreattack.com">https://spectreattack.com</a></li>
<li><a href="https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with-side.html">https://googleprojectzero.blogspot.com/2018/01/reading-privileged-memory-with-side.html</a></li>
<li><a href="https://www.filamentgroup.com/lab/performance-rwd.html">https://www.filamentgroup.com/lab/performance-rwd.html</a></li>
<li><a href="https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-02#section-5.3.7">https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-02#section-5.3.7</a></li>
<li><a href="https://www.owasp.org/index.php/HttpOnly">https://www.owasp.org/index.php/HttpOnly</a></li>
<li><a href="https://sites.google.com/a/chromium.org/dev/Home/chromium-security/ssca">https://sites.google.com/a/chromium.org/dev/Home/chromium-security/ssca</a></li>
<li><a href="https://www.chromium.org/developers/design-documents/oop-iframes">https://www.chromium.org/developers/design-documents/oop-iframes</a></li>
<li><a href="https://sites.google.com/a/chromium.org/dev/developers/design-documents/process-models#Supported_Models">https://sites.google.com/a/chromium.org/dev/developers/design-documents/process-models#Supported_Models</a></li>
</ol>
Can Preload Cut the Mustard?2018-01-02T19:00:00-05:00<h1>Can Preload Cut the Mustard?</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> 1/3/2018</p>
<p>Here at FG, we’re always looking out for new ways to simplify and improve the way we build websites. Sometimes our experiments result in big changes to our workflow, but even when they don’t, they usually broaden our understanding of the tools we can use. This post falls somewhere in the middle of the two.</p>
<p>More than a few times in the past year, we’ve mentioned <code>link[rel=preload]</code>, a new attribute that can be used on the HTML <code>link</code> element. “Preload” allows us to specify resources that should be fetched early and asynchronously to be subsequently used in the page. If you missed it, <a href="/lab/async-css.html">my recent post about <code>[rel=preload]</code></a> described how we use it to load parts of our CSS asynchronously, and how our <a href="https://github.com/filamentgroup/loadCSS/">loadCSS script</a> makes that work in browsers that don’t yet support the feature.</p>
<p>Recently, <a href="https://github.com/filamentgroup/loadCSS/issues/254">an interesting bug report</a> was filed in the loadCSS tracker. It said that in the Chrome browser, if a preload <code>link</code>'s <code>media</code> attribute does <strong>not</strong> match, Chrome will not fetch the file that it references. For example, given the following code, Chrome will not fetch <code>mystyles.css</code> at all if the browser is narrower than 800px, because the <code>media</code> does not match.</p>
<pre><code>&lt;link rel=&quot;preload&quot; href=&quot;my-styles.css&quot; as=&quot;style&quot; media=&quot;(min-width: 800px)&quot;&gt;
</code></pre>
<p>At first glance, I was pretty sure that this would be an issue related to Chrome itself and not loadCSS, because Chrome supports <code>preload</code> natively and loadCSS is written to exit quietly and do nothing in supporting browsers. But even if it was a Chrome issue, it still sounded fishy, because my understanding was that the browser will always fetch a stylesheet file even its <code>media</code> doesn’t match—and that behavior’s been useful for years, for loading a “print” stylesheet for example. So I did some testing.</p>
<p>As it turns out, the report was valid: Chrome will not fetch that stylesheet in those conditions. That got me thinking: is this expected or is it a bug? The <code>preload</code> spec was unfortunately not clear on this, so I filed <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=792038&amp;can=2&amp;q=preload%20media%20apply&amp;colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified">a bug in Chrome’s tracker</a> and then <a href="https://twitter.com/scottjehl/status/938078500069748736">took to the Twitters</a> to ask around.</p>
<p>As it turns out, the behavior is expected, and I was happily surprised! I’ll explain <em>why</em> in a minute…</p>
<h2 id="the-conditional-fetch">The Conditional Fetch <a class="direct-link" href="#the-conditional-fetch" aria-hidden="true">#</a></h2>
<p>One of the primary patterns in building resilient websites is to use feature tests to make decisions about how to enhance the user interface in each browser. For a number of years, we’ve employed feature tests to qualify the way we load and/or apply enhancements to the page, and most recently we’ve standardized on an approach <a href="http://responsivenews.co.uk/post/18948466399/cutting-the-mustard">dubbed</a> &quot;Cutting the Mustard, &quot; which goes like this:</p>
<ol>
<li>Perform a quick diagnostic test to see if the browser supports a few “modern” features</li>
<li>If so, proceed to make broad modern enhancements to the page</li>
</ol>
<p>…and in code, that could look like this, using our trusty old <a href="https://github.com/filamentgroup/loadJS/">loadJS script</a> to perform the fetch:</p>
<pre><code>if( document.querySelector &amp;&amp; localStorage ){
// load a pile of JavaScript
loadJS( &quot;my-enhancements.js&quot; );
}
</code></pre>
<p>…or even, perhaps like this, to load a particular JS file in only wider viewports:</p>
<pre><code>if( matchMedia( &quot;(min-width: 800px)&quot; ).matches ){
// load a pile of JavaScript
loadJS( &quot;my-enhancements.js&quot; );
}
</code></pre>
<p>This approach works well, but from a maintenance perspective, it’s a little obtuse, and from a performance perspective, it requires parsing and running JavaScript before any request can be made. A declarative HTML approach would certainly be clearer, but the usual HTML methods of loading a script have never offered a means of qualifying whether a script should be fetched or not.</p>
<p>Could <code>link[rel=preload]</code> provide a better alternative? Maybe!</p>
<h2 id="cutting-the-mustard-with-preload">Cutting the Mustard with Preload <a class="direct-link" href="#cutting-the-mustard-with-preload" aria-hidden="true">#</a></h2>
<p><code>link[rel=preload]</code> is designed to load many different types of files, not just CSS, so we can use it to reference the JavaScript file that we’d like to conditionally load. As we found above in the situation with the CSS file, we can qualify the conditions in which the file should be loaded by using the <code>link</code>‘s’ <code>media</code> attribute. Admittedly, the <code>media</code> attribute is not nearly as flexible as JavaScript is for feature testing, but it does allow us to test CSS media queries, at least.</p>
<p>So here is how we can use <code>preload</code> to start to reproduce the second code example above:</p>
<pre><code>&lt;link rel=&quot;preload&quot; href=&quot;my-enhancements.js&quot; as=&quot;script&quot; media=&quot;(min-width: 800px)&quot;&gt;
</code></pre>
<p>Given the HTML above, a supporting browser will fetch <code>my-enhancements.js</code> <em>only</em> if the viewport is wider than 800px. Neat! Unfortunately, <code>link[preload]</code> <em>only fetches</em> a file, but does not evaluate it or apply it to the page. It just puts it into a local cache, ready for later use. To apply the file to the page, we need an <code>onload</code> event handler, and since we’re loading a JavaScript file, we’ll need to create a <code>script</code> element that we can use to execute the script once it loads. Here’s how that’d look…</p>
<pre><code>
&lt;script&gt;
function applyScript(src){
var js = document.createElement( &quot;script&quot; );
js.src = src;
document.head.insertBefore( js, document.head.firstChild );
}
&lt;/script&gt;
&lt;link rel=&quot;preload&quot; href=&quot;test.js&quot; as=&quot;script&quot; media=&quot;(min-width: 800px)&quot; onload=&quot;applyScript(this.href)&quot;&gt;
</code></pre>
<p>And here’s a demo page for the code above. It’ll load and evaluate the script in browsers that support preload, if the viewport is wider than 800px (You’ll know when the script loads because it will throw an alert message): <a href="https://filamentgroup.github.io/loadJS/test/preload.html">Preload Cut the Mustard Demo 1</a></p>
<h2 id="worth-it%3F">Worth it? <a class="direct-link" href="#worth-it%3F" aria-hidden="true">#</a></h2>
<p>I think it’s unfortunate that <code>link[rel=preload]</code> offers no declarative means of evaluating a JavaScript file (or CSS either for that matter) on arrival, because we’re now back to using custom JavaScript to apply the file to the page. Still, there’s still an advantage here worth considering: <code>link[rel=preload]</code> will start to download the file as early as possible in the page loading process (unlike fetching the file via scripting alone), and that could make for a slightly more efficient process of enhancing a page.</p>
<p>That said, while we can indeed “cut the mustard” using <code>link[rel=preload]</code>, browser support for “preload” is still not very broad. If we wanted to use it to qualify our script loading across all browsers, we’d need to test for support and provide a fallback… and that means a little more JavaScript. We’re in the weeds already, so let’s consider how that code might look.</p>
<pre><code>
&lt;script&gt;
// preload support test
var preloadSupported = (function() {
try {
return document.createElement(&quot;link&quot;).relList.supports(&quot;preload&quot;);
} catch (e) {
return false;
}
})();
// apply on load
function applyScript(src) {
var js = document.createElement(&quot;script&quot;);
js.src = src;
document.head.insertBefore(js, document.head.firstChild);
}
// polyfill
if (!preloadSupported) {
var links = document.getElementsByTagName(&quot;link&quot;);
for (var i = 0; i &lt; links.length; i++) {
var link = links[i];
// qualify links to those with rel=preload and as=style attrs
if (link.rel === &quot;preload&quot; &amp;&amp; link.getAttribute(&quot;as&quot;) === &quot;script&quot; &amp;&amp; !link.getAttribute(&quot;data-loaded&quot;)) {
// prevent rerunning on link
link.setAttribute(&quot;data-loaded&quot;, true);
// if no media specified or media does match
if (!link.media || matchMedia(link.media).matches) {
applyScript(link.href);
}
}
}
}
&lt;/script&gt;
&lt;link rel=&quot;preload&quot; href=&quot;test.js&quot; as=&quot;script&quot; media=&quot;(min-width: 800px)&quot; onload=&quot;applyScript(this.href)&quot;&gt;
</code></pre>
<p>…does anyone else smell burning rubber?</p>
<p>The polyfill adds an unfortunate amount of JavaScript, but as a proof of concept, it certainly seems to work! Here’s a demo page for the code above. It should load and run the script in most any browser, as long as the viewport is wider than 800px: <a href="https://filamentgroup.github.io/loadJS/test/preload-polyfilled.html">Preload Cut the Mustard Demo 2</a></p>
<p>So it’s possible, at least! And it’s useful to know that we can use <code>link[rel=preload]</code> with qualifiers, even if the amount of code involved means we may not choose to use this in any production setting soon. Perhaps some tests would show that the early-preload request makes it worthwhile though, and in the future, polyfilling the preload behavior will become less necessary too.</p>
<p>Until then, we’ll likely keep cutting our mustard with an old but <a href="https://github.com/filamentgroup/loadJS/">trusty knife</a>.</p>
Accessible Links Re:visited2017-12-05T19:00:00-05:00<h1>Accessible Links Re:visited</h1>
<p>Posted by <a href="/about/#maggie-wachs">Maggie</a> 12/6/2017</p>
<p>We and our clients often tend toward simple and understated style, but sometimes that simplicity doesn’t serve all audiences as well as we would like. Recently we helped develop a site for a client who wanted a clean aesthetic that was not too busy. When the site went live, they conducted an accessibility audit, and we noticed an unfortunate trend in the results: links in running text were undetectable; focus states were sometimes obscure or unclear; and some links’ purpose was confusing when spoken aloud by certain screen readers. It was a good reminder that integrating accessibility best practices and validating code will get us closer, but won’t guarantee a quality experience navigating a site with keystrokes and auditory cues.</p>
<p>The recipe for accessible links seems self-evident: they need to have good affordance, visually change when focused, and clearly convey to screen readers and conventional browsers alike what content the link will open or where the user will go. But we discovered during this audit that even small missteps in how we style links and write link text can mean the difference between a usable and unusable experience. Here’s what we learned.</p>
<p>(Example markup mentioned in this article is also in <a href="/examples/a11y-links/">this demo page</a>.)</p>
<h2 id="color-contrast-and-affordance">Color contrast and affordance <a class="direct-link" href="#color-contrast-and-affordance" aria-hidden="true">#</a></h2>
<p>The <a href="https://www.w3.org/TR/WCAG20/#visual-audio-contrast-without-color">WCAG 2.0 guidelines</a> state that, at a minimum:</p>
<blockquote>
<p>Color is not used as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element.</p>
</blockquote>
<p>When links share the same size, weight, style, and font face as surrounding text, they lack affordance, so we usually add an underline or weight shift to make links discernable to users with low vision. But links styled to stand out in a paragraph don’t always work with our client’s design, and sometimes we have to find a middle ground that balances the brand aesthetic with accessibility.</p>
<h3 id="check-your-contrast">Check your contrast <a class="direct-link" href="#check-your-contrast" aria-hidden="true">#</a></h3>
<p>This is where color contrast comes in. When additional link styling is not an option, sufficient contrast between the link and text colors is essential. WCAG helpfully provides measurable criteria in the form of color contrast ratios that we can use for assessing our designs. Level AA success criteria include the following ratios:</p>
<ul>
<li><a href="https://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast">4.5:1</a> when compared with the page background</li>
<li><a href="https://www.w3.org/TR/2008/NOTE-WCAG20-TECHS-20081211/G183">3:1</a> when compared with surrounding text</li>
</ul>
<p><em>Aside: WCAG 2.0 organizes recommendations for achieving accessibility into <a href="https://www.w3.org/TR/WCAG20/#conformance-reqs">three levels of conformance (A, AA, and AAA)</a>, which define baseline through advanced success criteria, and each level builds upon the previous. Think of Level A as the bare minimum, and AAA as the holy grail of accessibility. We tend to shoot for Level AA compliance, which strikes a balance between the two and addresses the most prevalent concerns.</em></p>
<p>Several tools exist that will calculate the color contrast ratio for you (<a href="https://www.google.com/search?q=color+contrast+analyzer&amp;oq=color+contrast+analyzer&amp;aqs=chrome..69i57j0l5.8631j0j7&amp;sourceid=chrome&amp;ie=UTF-8">we Googled them for you</a>). We like <a href="https://webaim.org/resources/contrastchecker/">WebAIM’s Color Contrast Checker</a> for it’s ease of use and clear feedback. Just plug in the link color for foreground, and text color for background.</p>
<figure>
<img src="/images/a11y-links/color-contrast-checker.png" alt="">
<figcaption>WebAIM's Color Contrast Checker</figcaption>
</figure>
<p>We recommend using a tool like this manually instead of relying on an automated validator like <a href="http://wave.webaim.org/">WAVE</a>, which doesn’t measure the link-against-text color contrast.</p>
<h3 id="underline-subtly">Underline subtly <a class="direct-link" href="#underline-subtly" aria-hidden="true">#</a></h3>
<p>While adequate contrast between link and text colors helps affordance, it doesn’t replace what a style shift can do — especially when you consider users who cannot detect color at all. The <code>text-decoration</code> property provides a nice compromise here because you can <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/text-decoration">alter the underline with style and color values</a>. For the Filament Group web site, we added a subtle light blue underline to our bold links for greater affordance:</p>
<pre><code>text-decoration-line: underline;
text-decoration-style: solid;
text-decoration-color: #8cc8ff;
// or shorthand:
text-decoration: underline solid #8cc8ff;
</code></pre>
<p>In browsers that don’t fully support the <code>text-decoration-color</code> or <code>-style</code> properties, the underline will render as a solid line that matches the link color — as if you used the simpler <code>text-decoration: underline</code>.</p>
<h3 id="edit-focus-state%2C-don%E2%80%99t-remove-it">Edit focus state, don’t remove it <a class="direct-link" href="#edit-focus-state%2C-don%E2%80%99t-remove-it" aria-hidden="true">#</a></h3>
<p>Like good affordance, a clear focus state is critical to finding your way around a web page with a screen reader or when using tabs (<a href="https://www.w3.org/TR/UNDERSTANDING-WCAG20/navigation-mechanisms-focus-visible.html">and is also required for Level AA compliance</a>). All focusable elements — links, buttons, form elements — should have a noticeable visual shift when focused to provide a “you are here” reference point. (Remove the focus state from a page using your web inspector, and then tab through it and periodially hit “Enter” to see what happens.)</p>
<p>Conveniently, browsers provide a default focus state using the CSS <code>outline</code> property. It tends to look like a blue fuzzy neon sign around the element.</p>
<figure>
<img src="/images/a11y-links/default-focus.png" alt="">
<figcaption>Default focus state.</figcaption>
</figure>
<p>As illustrated, the outline property doesn’t listen to border radius, so the focus state looks mismatched. For the sake of aesthetics, in the past we’ve globally negated that <code>outline</code> rule in favor of a more subtle focus style per element — and have since learned our lesson. When you kill the <code>outline</code> property at the global level, it’s not possible to reinstate it on an element-by-element basis across all browsers. Once it’s gone, it’s gone, and if you don’t provide an alternate global style (like a <code>border</code> or <code>box-shadow</code>) in its place, you risk a confusing, focus-less experience.</p>
<p>A better solution is to keep the global <code>outline</code> rule, and edit the focus state per element only as needed. For our search input, we replaced the <code>outline</code> with a <code>border</code> and <code>box-shadow</code> that will respect the border radius, and kept the blue color because it’s <a href="https://www.w3.org/TR/2008/NOTE-WCAG20-TECHS-20081211/working-examples/G183/link-contrast.html">most friendly to colorblind users</a>:</p>
<figure>
<img src="/images/a11y-links/edited-focus.png" alt="">
<figcaption>Focus tailored to the design.</figcaption>
</figure>
<p>As an added security measure, we recommend wrapping the replacement <code>box-shadow</code> focus styles in an <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@supports"><code>@supports</code></a> rule to ensure that they’re only applied by browsers that will render them:</p>
<pre><code>@supports (box-shadow: 0 0 0 3px rgba( 180, 222, 250, .75 )) {
input[type=text]:focus {
outline: none;
border-color: #2186CA;
box-shadow: 0 0 0 3px rgba( 180, 222, 250, .75 );
}
}
</code></pre>
<p>Related: Make sure hidden focusable elements are removed from the tab order using <code>display: none</code> or by programmatically assigning a <code>tabindex=&quot;-1&quot;</code> attribute so screen readers don’t stumble upon them. Read more about creating an accessible tab/focus order in <a href="https://www.filamentgroup.com/lab/accessible-responsive.html#focus">Maintaining Accessibility in a Responsive World</a>.</p>
<h2 id="write-text-that-reads-well">Write text that reads well <a class="direct-link" href="#write-text-that-reads-well" aria-hidden="true">#</a></h2>
<p>Screen readers speak in robotic voices in varying clarity and speed, and like your average GPS apps, occassionally misprounounce words. Text formatting can affect how a screen reader interprets the content, for better or worse.</p>
<h3 id="use-punctuation">Use punctuation <a class="direct-link" href="#use-punctuation" aria-hidden="true">#</a></h3>
<p>When a screen reader encounters inline tags like <code>span</code> or <code>strong</code> nested within block-level elements or links, it speaks their content as if it’s running text; no pauses between phrases are inferred. At least one screen reader that we know of, JAWS for Windows, will disgregard white space within and between a <code>span</code> and its neighbors. We discovered this when using inline tags to create visual texture in marketing links. We started with this:</p>
<pre><code>&lt;div class="marketing-block">
&lt;a href="#">Winter is coming &lt;span>Shop holiday gifts&lt;/span>&lt;/a>
&lt;/div></code></pre>
<p>We used visual styles to separate the phrases (weight shift, text on different lines), but those things are completely ignored by screen readers that rely on text characters (spaces, punctuation) to interpret pauses between words. VoiceOver (Mac, iOS) reads it as a single sentence, “Winter is coming shop holiday gifts.” JAWS for Windows will ignore all white space in/around the <code>span</code> and read, “Winter is comingshop holiday gifts.”</p>
<p>Adding punctuation significantly improves how this link sounds. When you add a period after the first section, the text is read with a distinct pause between phrases: “Winter is coming. [pause] Shop holiday gifts”. Adding punctuation may not work visually, so you can <a href="https://www.filamentgroup.com/lab/accessible-responsive.html#hiding">hide it in a way that preserves it for screen readers</a>. Examples on this page use a class for this named <code>a11y-only</code>:</p>
<pre><code>&lt;div class="marketing-block">
&lt;a href="#">Winter is coming&lt;span class="a11y-only">.&lt;/span> &lt;span>Shop holiday gifts&lt;/span>&lt;/a>
&lt;/div></code></pre>
<p><em>We recorded VoiceOver reading this link with no punctuation, then with hidden punctuation:</em></p>
<video controls>
<source src="/images/a11y-links/links-punctuation-screen.ogv" type="video/ogg">
<source src="/images/a11y-links/links-punctuation-screen.webm" type="video/webm">
<source src="/images/a11y-links/links-punctuation-screen.mp4" type="video/mp4">
Video is unavailable in this browser.
</video>
<p><strong>A notable exception:</strong> NVDA (Windows) will read the hidden period aloud as “clickable dot,” which is not ideal. Users can edit how NVDA reads punctuation in its <a href="https://www.nvaccess.org/files/nvda/documentation/userGuide.html#SymbolPronunciation">Preferences &gt; Punctuation/symbol pronunciation dialog</a>, however we recommend testing your code using NVDA’s default settings to determine if this feature affects your content’s comprehension.</p>
<h3 id="add-non-breaking-spaces">Add non-breaking spaces <a class="direct-link" href="#add-non-breaking-spaces" aria-hidden="true">#</a></h3>
<p>To avoid conjoined words in JAWS (i.e., “Winter is <em>comingshop</em> holiday gifts”), use non-breaking spaces between inline tags and their siblings:</p>
<pre><code>&lt;div class="marketing-block">
&lt;a href="#">Shop&nspb;&lt;span>holiday gifts&lt;/span>&lt;/a>
&lt;/div></code></pre>
<h3 id="state-the-destination">State the destination <a class="direct-link" href="#state-the-destination" aria-hidden="true">#</a></h3>
<p>Link text should say where it’s going to take you. In in the example above, you know you’ll go to the holiday gift shop. Ambiguity can creep in when we rely on other visual cues for defining a link’s purpose or destination that aren’t accessible to screen readers. For example, let’s say you have an image of a Google map followed by a link that says, “Get directions.” Now hide the map, or replace it with <code>alt</code> text read aloud. Where does “Get directions” go? Does it open a modal, or navigate to elsewhere on the page, or to a new page?</p>
<p>Include text for screen readers that eliminates any guesswork. “Open Google Maps to Get directions” is much clearer. If you need to keep the rendered text succint, hide helper phrases for screen readers from view (we’re using <code>a11y-only</code> again here):</p>
<pre><code>&lt;a href="[url]">&lt;span class="a11y-only">Open Google Maps to&amp;nbsp;&lt;/span>Get directions&lt;/a></code></pre>
<p>(And remember to use spaces and punctuation!)</p>
<h2 id="link-block-level-elements-with-care">Link block level elements with care <a class="direct-link" href="#link-block-level-elements-with-care" aria-hidden="true">#</a></h2>
<p>When HTML5 was introduced, links around block level elements became valid markup as long as you followed the rules of proper syntax and tag nesting. Unfortunately, links with multiple lines of content can sometimes be confusing for screen reader users, especially those using JAWS or other Windows-based screen readers which may <a href="https://developer.paciellogroup.com/blog/2011/06/html5-accessibility-chops-block-links/">treat text as separate links, or ignore it altogether</a>. Take the following link structure, which navigates to a “new feature” page:</p>
<pre><code>&lt;a href="[url]">
&lt;div>
&lt;h3>Introducing a new feature&lt;/h3>
&lt;p>This new feature will change your life and make everything wonderful.&lt;/p>
&lt;p>Learn more&lt;/p>
&lt;/div>
&lt;/a></code></pre>
<p>Linking the entire block gives mouse- and sighted keyboard users a larger hit area, but VoiceOver users hear one long run-on sentence: “Introducing a new feature this new feature will change your life and make everything wonderful. [pause] Learn more”. NVDA reads it as 3 separate links: “Introducing…”, “This new feature…”, and “Learn more”. In JAWS for Windows, content inside the <code>div</code> is, in a word, confusing.</p>
<p>That’s not to say block element links should be avoided. We just need to make sure link content is accessible and the call to action is clear. If you can change the markup structure, we recommend replacing block-level with inline elements (you can reapply block-level properties with CSS), and then follow the punctuation and spacing guidelines above to improve how the text is read aloud.</p>
<pre><code>&lt;a href="[url]">
&lt;span>
&lt;span class="title">Introducing a new feature&lt;/span>
&lt;span>This new feature will change your life and make everything wonderful.&amp;nbsp;&lt;/span>
&lt;span>Learn more&lt;/span>
&lt;/span>
&lt;/a></code></pre>
<h2 id="wait%2C-there%E2%80%99s-more%E2%80%A6">Wait, there’s more… <a class="direct-link" href="#wait%2C-there%E2%80%99s-more%E2%80%A6" aria-hidden="true">#</a></h2>
<p>We hope you found this review as helpful as we did when going through the auditing process. Integrating the screen reader experience into development has helped us discover areas that are easy to overlook, especially when considering all the boxes that we need to check when creating a usable experience (clean design, responsiveness, performance). Stay tuned for more posts on what we’ve learned!</p>
<p>Want to chat about this post? Find us at <a href="https://twitter.com/filamentgroup">@filamentgroup</a> on Twitter. (And of course, if you’d like us to help you work through a challenge like this for your company’s site or app, <a href="mailto:hello@filamentgroup.com">get in touch</a>!)</p>
<p><em>If you haven’t used a screen reader or observed one, try <a href="https://www.apple.com/accessibility/mac/vision/">VoiceOver</a>, which comes with every Mac or iOS device, or <a href="https://www.nvaccess.org/">NVDA</a>, the free screen reader for Windows. There are also many examples of screen readers in use on YouTube, or you can try <a href="https://webaim.org/simulations/screenreader">WebAIM’s simulator</a>.</em></p>
<h2 id="acknowledgment">Acknowledgment <a class="direct-link" href="#acknowledgment" aria-hidden="true">#</a></h2>
<p>Many thanks to Reinhard Stebner for his contribution to this article and for his insights which are helping us become more adept at developing for accessibility. Inquiries for consulting services can be directed to Reinhard at <a href="https://www.linkedin.com/in/reinhardstebner/">LinkedIn</a>, <a href="reinhard.stebner@gmail.com">reinhard.stebner@gmail.com</a>, or by phone: (571) 214-7319.</p>
Modern Asynchronous CSS Loading2017-11-29T19:00:00-05:00<h1>Modern Asynchronous CSS Loading</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> on 11/30/2017</p>
<p>The simplest way to load a CSS file in an HTML document is to use a <code>link</code> element with <code>rel=&quot;stylesheet&quot;</code>:</p>
<pre><code>&lt;link rel=&quot;stylesheet&quot; href=&quot;mycssfile.css&quot;&gt;
</code></pre>
<p>Referencing CSS this way works great, but it comes with a downside: it’s synchronous. In other words, with a typical stylesheet link like this, the browser stops rendering subsequent portions of the page while it requests, downloads, and parses the file. Sometimes that blocking can be desirable because we don’t want the browser to render the page before it has the CSS it needs. But not all of our CSS files are critical enough to delay access to the content, which is why we highly <a href="/lab/modernizing-delivery.html">recommend prioritizing and streamlining assets for fast, resilient delivery</a>.</p>
<p>To load less-critical CSS files without blocking page rendering, we need to load them asynchronously.</p>
<h2 id="async-css-loading-approaches">Async CSS Loading Approaches <a class="direct-link" href="#async-css-loading-approaches" aria-hidden="true">#</a></h2>
<p>Historically, there are several ways to make a browser load CSS asynchronously, though none are quite as simple as you might expect.</p>
<p>One way (which works in modern browsers, at least) is to use JavaScript to create and insert a stylesheet link into the DOM:</p>
<pre><code>// make a stylesheet link
var myCSS = document.createElement( &quot;link&quot; );
myCSS.rel = &quot;stylesheet&quot;;
myCSS.href = &quot;mystyles.css&quot;;
// insert it at the end of the head in a legacy-friendly manner
document.head.insertBefore( myCSS, document.head.childNodes[ document.head.childNodes.length - 1 ].nextSibling );
</code></pre>
<p>That last line’s a doozy!</p>
<p>Another way is to set a stylesheet <code>link</code>'s <code>media</code> attribute to a media type (or query) that does not match the user’s current browsing environment, say perhaps <code>media=&quot;print&quot;</code>, or even something unrecognizable like <code>media=&quot;nope!&quot;</code>. Browsers will consider stylesheets for inapplicable media to be low-priority, and download them without blocking page rendering. That’s good to know, but in order to <em>display</em> the stylesheet’s rules once it loads, we need to use a JavaScript <code>onload</code> event handler to toggle the <code>media</code> value to something that matches the user’s browsing environment, like <code>screen</code> or <code>all</code>:</p>
<pre><code>&lt;link rel=&quot;stylesheet&quot; href=&quot;mystyles.css&quot; media=&quot;nope!&quot; onload=&quot;this.media='all'&quot;&gt;
</code></pre>
<p><em>As a side note: we use a combination of the tricks above internally in our loadCSS.js library to handle asynchronous CSS loading, along with additional workarounds for old/still-active browsers that don’t support <code>onload</code> events on <code>link</code> elements.</em></p>
<p>Similar to the media toggle approach, we could also load CSS asynchronously by marking a <code>link</code> as an <code>alternate</code> stylesheet (designed to offer the user alternate presentations of a site), and then using JavaScript to toggle the <code>rel</code> attribute back to <code>stylesheet</code> when the file loads:</p>
<pre><code>&lt;link rel=&quot;alternate stylesheet&quot; href=&quot;mystyles.css&quot; onload=&quot;this.rel='stylesheet'&quot;&gt;
</code></pre>
<p>The methods above do the job, but they share an unfortunate quality: they rely on JavaScript (not to mention knowledge of nuanced browser parsing behavior) to <em>indirectly</em> achieve the desired effect…</p>
<h2 id="a-modern-approach">A Modern Approach <a class="direct-link" href="#a-modern-approach" aria-hidden="true">#</a></h2>
<p>Thankfully, there’s now a web standard that is designed <em>specifically</em> for loading resources like CSS asynchronously: <a href="https://www.w3.org/TR/preload/"><code>rel=&quot;preload&quot;</code></a>. Finally, we can load asynchronous CSS without JavaScript workarounds! Just kidding; perhaps surprisingly, even this method requires an <code>onload</code> event handler to make it work as we need. Still, it’s our best option.</p>
<p>Here’s an example of how we can use <code>rel=&quot;preload&quot;</code> to load and apply a CSS file asynchronously (in a browser that supports it):</p>
<pre><code>&lt;link rel=&quot;preload&quot; href=&quot;mystyles.css&quot; as=&quot;style&quot; onload=&quot;this.rel='stylesheet'&quot;&gt;
</code></pre>
<p>Much like other attribute-toggling approaches above, <code>rel=&quot;preload&quot;</code> will cause supporting browsers to download–but not <em>apply</em>–the referenced file, so again we need an <code>onload</code> handler to set the <code>rel</code> attribute to <code>stylesheet</code> once it finishes loading. This may not look like a big improvement over other approaches, but one advantage that <code>rel=&quot;preload&quot;</code> brings is that supporting browsers will start downloading the file earlier than they would with say, a stylesheet with a non-matching media value.</p>
<h3 id="using-rel%3Dpreload-today-with-loadcss">Using rel=preload Today with loadCSS <a class="direct-link" href="#using-rel%3Dpreload-today-with-loadcss" aria-hidden="true">#</a></h3>
<p><a href="https://caniuse.com/#search=preload">Browser support for <code>rel=&quot;preload&quot;</code></a> is, well… I mean, at least Chrome supports it. Other major browsers have committed support too, though. In fact, Firefox 56 already <em>did</em> support <code>rel=&quot;preload&quot;</code>, but its implementation was buggy enough (<code>preload</code> only worked for files that were explicitly deemed cacheable) that Firefox 57 shipped with the feature disabled (support will return again in Firefox 59).</p>
<p>Fortunately, we can test and polyfill support for <code>rel=&quot;preload&quot;</code> to make sure it works everywhere. Our <a href="https://github.com/filamentgroup/loadCSS/">loadCSS project</a> offers a script called <a href="https://github.com/filamentgroup/loadCSS/blob/v2.0.1/src/cssrelpreload.js">cssrelpreload.js</a>, which makes <code>rel=&quot;preload&quot;</code> work for CSS files in browsers that don’t support it natively (and it knows to do nothing if the browser knows how to handle preload on its own).</p>
<p>You can find <a href="https://github.com/filamentgroup/loadCSS/tree/v2.0.1#loadcss">step-by-step usage instructions for using <code>cssrelpreload.js</code> in the project readme</a>, but in summary, including the script in your page either inline (or with server-push it if you’re able), will automatically ensure that any <code>link[rel=&quot;preload&quot;]</code> elements will work as expected in unsupporting browsers.</p>
<p>Here’s an example of how we use it, including a <code>noscript</code> fallback, just in case:</p>
<pre><code>&lt;link rel=&quot;preload&quot; href=&quot;mystyles.css&quot; as=&quot;style&quot; onload=&quot;this.rel=&apos;stylesheet&apos;&quot;&gt;
&lt;noscript&gt;&lt;link rel=&quot;stylesheet&quot; href=&quot;path/to/mystylesheet.css&quot;&gt;&lt;/noscript&gt;
&lt;script&gt;
/*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */ (function(){ ... }());
&lt;/script&gt;</code></pre>
<p><em><strong>NOTE:</strong> if you’ve used <a href="https://github.com/filamentgroup/loadCSS/blob/v2.0.1/src/cssrelpreload.js">cssrelpreload.js</a> before, the latest version (2.0.1) has some nice improvements, and it no longer depends on the <code>loadCSS.js</code> script, so you can stop including that file and save some precious kilobytes.</em></p>
<p>As with any of our open source projects, you can find loadCSS on <a href="https://github.com/filamentgroup/loadCSS/">Github</a> and on <a href="https://www.npmjs.com/package/fg-loadcss">NPM</a>. If you run into any issues, have questions, or want to contribute, please let us know in <a href="https://github.com/filamentgroup/loadCSS/issues">the issue tracker</a>.</p>
<p>Thanks!</p>
Keep It Snappy2017-07-13T20:00:00-04:00<h1>Keep It Snappy</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> and <a href="/about/#todd-parker">Todd</a> on 07/12/2017</p>
<p>As <a href="/lab/introducing-snapper.html">we’ve said before</a>, we’re excited about <a href="https://www.w3.org/TR/css-scroll-snap-1/">CSS Snap Points</a>, as it standardizes and simplifies a lot of the common “carousel” behavior that we’d otherwise need to mimic with a bunch of JavaScript. There are a number of great resources that describe how to use CSS Snap Points (e.g. <a href="https://css-tricks.com/introducing-css-scroll-snap-points/">Sarah Drasner’s CSS Tricks article</a>), however, <a href="https://twitter.com/toddmparker/status/883093579039735808">Todd here noticed</a> that many existing examples (including ours) didn’t seem to work in Safari on the current betas of iOS11.</p>
<p>After a bit of inspection, we found that Safari in iOS11 sets the new <code>scroll-snap-align</code> CSS property to a value of <code>none</code> when it is not explicitly defined in a stylesheet, and as far as I can tell that means a snap points carousel will not snap as expected. This is a shame since most CSS snap points implementations we can find on the web were created before that property even existed!</p>
<p>If <code>scroll-snap-align</code> is new to you, don’t feel bad—it was added to the spec just this year. Here’s how the w3c spec describes it:</p>
<blockquote>
<p>The <code>scroll-snap-align</code> property specifies the box’s snap position as an alignment of its snap area (as the alignment subject) within its snap container’s snapport (as the alignment container).</p>
</blockquote>
<p><em>Coincidentally, snapport is also an acceptable reply when someone sneezes… here at Filament at least, as of today.</em></p>
<p>Anyway. There is a quick fix: add a <code>scroll-snap-align</code> rule to the snappable child items in your scroller (adjusting your selectors accordingly).</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"> <span class="token selector">#my-scrolling-element > .child</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token property">scroll-snap-align</span><span class="token punctuation">:</span> start<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token punctuation">}</span></span></code></pre>
<p>We found that <code>start</code> is typically the value we need, but other values like <code>end</code> or <code>center</code> might make sense in some cases.</p>
<p>Based on my observations, it seems that iOS Safari is the first browser to recognize this property. Firefox, another CSS Snap Points supporting browser, does not yet recognize it, for example. However, I’d expect that whenever it does, it too will follow the spec and utilize this default value of <code>none</code>.</p>
<p>Hopefully, the Safari team (or the w3c spec authors?) will decide to change the default of this property to something like <code>start</code>, but just in case, it seems like a good idea to add it explicitly today. And by the way, if you happen to use our Snapper carousel, you’ll find this property is already added to the CSS in version 3.0.0.</p>
Presentation: Accessibility in a Responsive World, A11Y Days 20172017-05-15T20:00:00-04:00<h1>Presentation: Accessibility in a Responsive World, A11Y Days 2017</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> on 05/16/2017</p>
<p>This April, I was thrilled to be given the opportunity to present at <a href="http://www.funka.com/en/we-offer/funka-accessibility-days/accessibility-days-2017/">Funka’s annual Accessibility Days Conference</a> in Stockholm. The following is a transcript and slides from my presentation, Accessibility in a Responsive World.</p>
<hr>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.001.webp"><img src="/images/funka2017/images.001-480.jpg" srcset="/images/funka2017/images.001-480.jpg 480w, /images/funka2017/images.001.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt=""></picture>
</figure>
<blockquote>
<p>Good afternoon, everyone! It’s such an honor for me to be here in Stockholm at this event. Thanks so much for joining me for this technical session.</p>
</blockquote>
<blockquote>
<p>Today I’m going to talk about accessibility within the context of responsive web design.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.002.webp"><img src="/images/funka2017/images.002-480.jpg" srcset="/images/funka2017/images.002-480.jpg 480w, /images/funka2017/images.002.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="Being accessible"></picture>
</figure>
<blockquote>
<p>I work for a consultancy in the US called Filament Group.</p>
</blockquote>
<blockquote>
<p>At Filament, we design &amp; develop websites and applications for clients both large and small. And on behalf of our clients, our goal has long been to build sites that are accessible to as many people as possible.</p>
</blockquote>
<blockquote>
<p>In that aim, we’ve found that being “accessible” means many things to us, to our clients, and to users.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.003.webp"><img src="/images/funka2017/images.003-480.jpg" srcset="/images/funka2017/images.003-480.jpg 480w, /images/funka2017/images.003.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="closeup of several people charging their smartphones"></picture>
<figcaption><a href="http://mobile.reuters.com/article/topNews/idUSKCN0Q912620150804?irpc=932">Visit image source</a></figcaption>
</figure>
<blockquote>
<p>First, it means that a website must be able to be reached in the first place, from anywhere in the world, and quickly—particularly in slow and spotty mobile networks.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.004.webp"><img src="/images/funka2017/images.004-480.jpg" srcset="/images/funka2017/images.004-480.jpg 480w, /images/funka2017/images.004.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="3 people using smartphones"></picture>
<figcaption><a href="https://www.flickr.com/photos/esthervargasc/9657863733">Visit image source</a></figcaption>
</figure>
<blockquote>
<p>And that alone takes special care to do well.</p>
</blockquote>
<blockquote>
<p>Many aspects of web delivery are out of our control, but we can do a lot to increase the odds of a fast &amp; useful delivery. So I like to think of performance and resilience as the earliest accessibility metrics to care about. Speed improves access. And to build resilient sites, we need to plan for failure, even expect it, and take steps to ensure our code can gracefully tolerate faults that can render a site unusable, or prevent it from rendering at all.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.005.webp"><img src="/images/funka2017/images.005-480.jpg" srcset="/images/funka2017/images.005-480.jpg 480w, /images/funka2017/images.005.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="Pile of tablets and phones"></picture>
<figcaption><a href="https://www.flickr.com/photos/adactio/6153522068/">Visit image source</a></figcaption>
</figure>
<blockquote>
<p>Building accessibly to us also means that once the code is delivered, the website should be usable and intuitive and feel appropriate to folks using any device, browser, viewport size, and assistive technology.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/funka2017/images.006-480.jpg" srcset="/images/funka2017/images.006-480.jpg 480w, /images/funka2017/images.006.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>Vineyard Vines, an example of a Responsive layout.</figcaption>
</figure>
<blockquote>
<p>From a screen size perspective, that means designing interfaces that are fluid and contextual, to feel at home on any device, and this is something Responsive Web Design—the pairing of fluid layout &amp; images, with CSS media queries—helps us do quite well.</p>
</blockquote>
<blockquote>
<p>It’s been amazing to watch responsive design go from a proof of concept that a small number of us used on our blogs to what’s now almost an expected quality of a website.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.007.webp"><img src="/images/funka2017/images.007-480.jpg" srcset="/images/funka2017/images.007-480.jpg 480w, /images/funka2017/images.007.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="Hands typing"></picture>
<figcaption><a href="https://www.flickr.com/photos/anonymouscollective/4263193267/">Visit image source</a></figcaption>
</figure>
<blockquote>
<p>Of course, to truly work across all of these different screens, responsive interfaces need to be inclusive to common input modes as well. Like keyboard, touch, and mouse.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.008.webp"><img src="/images/funka2017/images.008-480.jpg" srcset="/images/funka2017/images.008-480.jpg 480w, /images/funka2017/images.008.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="image of iPhone VoiceOver dial in use"></picture>
<figcaption>iOS VoiceOver's dial interface</figcaption>
</figure>
<blockquote>
<p>And we can go a long way towards that goal by using meaningful HTML markup to ensure an accessible experience from the start, such as using real focusable elements for buttons, and layering in ARIA attributes when necessary to improve the ways that markup is communicated to assistive technology.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.009.webp"><img src="/images/funka2017/images.009-480.jpg" srcset="/images/funka2017/images.009-480.jpg 480w, /images/funka2017/images.009.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="a woman using smartphone"></picture>
<figcaption><a href="https://www.flickr.com/photos/johnragai/15823751904">Visit image source</a></figcaption>
</figure>
<blockquote>
<p>And often most critically, it means applying enhancements defensively, in a qualified manner.</p>
</blockquote>
<blockquote>
<p>Features and constraints vary across browsers, devices, and networks, and the code we deliver should accommodate.</p>
</blockquote>
<blockquote>
<p>We need to take care to use techniques that shield users from downloading code that their device won’t end up using, or images that are larger than necessary for their display, and we must apply enhancements progressively, and cautiously, so that we don’t unintentionally break a page that was already usable in its initial delivered form.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.010.webp"><img src="/images/funka2017/images.010-480.jpg" srcset="/images/funka2017/images.010-480.jpg 480w, /images/funka2017/images.010.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt="Quote from Derek Featherstone: Creating great experiences for a particular subset of our users—people with disabilities—results in better designs for everyone"></picture>
<figcaption><a href="http://simplyaccessible.com/">http://simplyaccessible.com/</a></figcaption>
</figure>
<blockquote>
<p>But, often than not, I do find that the practices we use to achieve all of these goals tend to play very nicely together.</p>
</blockquote>
<blockquote>
<p>That’s really a testament to how well web standards have been planned. Carefully addressing one use case almost always betters the experience of the others as well.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.011.webp"><img src="/images/funka2017/images.011-480.jpg" srcset="/images/funka2017/images.011-480.jpg 480w, /images/funka2017/images.011.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="Conflicts of Access"></picture>
</figure>
<blockquote>
<p>That said, during the process of developing a site, one design requirement, optimization, or priority can sometimes complicate or conflict with another, or at least competes with the way we’re used to doing things. And when that happens, we ideally want to find new ways to satisfy all our priorities.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.012.webp"><img src="/images/funka2017/images.012-480.jpg" srcset="/images/funka2017/images.012-480.jpg 480w, /images/funka2017/images.012.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="person testing a site on smartphones"></picture>
<figcaption><a href="https://www.flickr.com/photos/adactio/12674596614">Visit image source</a></figcaption>
</figure>
<blockquote>
<p>Recently, I’ve encountered some challenges at the intersection of responsive design and accessibility.</p>
</blockquote>
<blockquote>
<p>CSS itself gives us powerful control over the way our linear HTML is presented, and using it as designed can sometimes result in a disconnect between the markup that keyboard and assistive technology users interact with, and the way that markup is being presented on the screen. Additionally, CSS media queries enable us to shift between many of these potentially disconnected presentations of our HTML depending on the size of the viewport, which can compound the problem.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.013.webp"><img src="/images/funka2017/images.013-480.jpg" srcset="/images/funka2017/images.013-480.jpg 480w, /images/funka2017/images.013.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption>Two presentations of a collapsible nav control depending on viewport width.</figcaption>
</figure>
<blockquote>
<p>That disconnect can create barriers for many people.</p>
</blockquote>
<blockquote>
<p>More and more, we’re tasked with creating components that behave quite differently across devices. Inadequate pairing of visual &amp; aural interfaces at all variations of a UI can lead to confusion, particularly for sighted (and partially sighted) screen reader users.</p>
</blockquote>
<blockquote>
<p>And that’s perhaps an under-looked group. Our field typically discusses screen reader usage as it relates to blind people, but it’s easy to forget that folks use screen readers and other assistive tech for a far wider variety of additional reasons, from partial sightedness to cognitive disabilities, to all kinds of physical impairment (temporary and not).</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.014.webp"><img src="/images/funka2017/images.014-480.jpg" srcset="/images/funka2017/images.014-480.jpg 480w, /images/funka2017/images.014.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="Quote from Jeremy Keith: The web is not a platform.
It’s a continuum."></picture>
<figcaption><a href="https://adactio.com/journal/6692">Source</a></figcaption>
</figure>
<blockquote>
<p>Much like all means of accessing the web, assistive technology is no single platform, but rather a continuum, to quote Jeremy Keith… a continuum of use cases to consider.</p>
</blockquote>
<blockquote>
<p>As most accessibility guides will say, pairing the visual experience very closely with the experience that is communicated to assistive technology can eliminate a great number of problems for users throughout that continuum.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.015.webp"><img src="/images/funka2017/images.015-480.jpg" srcset="/images/funka2017/images.015-480.jpg 480w, /images/funka2017/images.015.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="Problem Areas"></picture>
</figure>
<blockquote>
<p>But achieving that is not always straightforward.</p>
</blockquote>
<blockquote>
<p>For the rest of this session, I’ll look at some of the challenges I’ve seen in accurately communicating the changing state of a responsive interface to assistive technology.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.016.webp"><img src="/images/funka2017/images.016-480.jpg" srcset="/images/funka2017/images.016-480.jpg 480w, /images/funka2017/images.016.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="Spatial & Behavioral Cues"></picture>
</figure>
<blockquote>
<p>A little while ago, I reached out on Twitter to ask about the challenges other developers encounter in creating accessible responsive layouts, and many of the answers related to problematic spatial and behavioral language in a UI.</p>
</blockquote>
<blockquote>
<p>I too had run into issues in this area.</p>
</blockquote>
<blockquote>
<p>Sometimes text cues in an interface reference the visual relationship of elements in a layout, and that text can be misleading if that layout changes across viewport sizes in a responsive design.</p>
</blockquote>
<figure class="emphasize">
<video controls poster="/images/funka2017/images.017-480.jpg">
<source src="/images/funka2017/video-2.m4v" type="video/mp4">
</video>
<figcaption>Screenshot of resizing layout</figcaption>
</figure>
<blockquote>
<p>Take this somewhat contrived example of a text caption that might appear below or to the right of an image depending on the viewport size. Referring to the image using terms like “above” or “left&quot; can be misleading unless we dynamically update it to match the layout, and that’s probably more complicated than it’s worth.</p>
</blockquote>
<blockquote>
<p>You could extrapolate this example to more complicated scenarios, particularly with info graphics. But if we try to avoid visual language entirely and instead say something like &quot;Pictured: …” or “Figure 1”, we can avoid those spatial issues before they crop up.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.018.webp"><img src="/images/funka2017/images.018-480.jpg" srcset="/images/funka2017/images.018-480.jpg 480w, /images/funka2017/images.018.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>Code example:</p>
<pre><code>
&lt;figure&gt;
&lt;img src=&quot;eiffel.jpg&quot; alt=&quot;Eiffel tower&quot;&gt;
<pre><code>&amp;lt;figcaption&amp;gt;Pictured, the Eiffel Tower, Is Illuminated in Green to Celebrate Paris Agreement&amp;apos;s Entry into Force&amp;lt;/figcaption&amp;gt;
</code></pre>
<p>&lt;/figure&gt;
</code></pre></p>
</figcaption>
</figure>
<blockquote>
<p>For cases like this, it also improves accessibility to use HTML elements that are designed for this use case of relating a caption to a figure.</p>
</blockquote>
<blockquote>
<p>The figure and figcaption elements are designed for this, as shown here, and their native semantics leave nothing for us to add.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.019.webp"><img src="/images/funka2017/images.019-480.jpg" srcset="/images/funka2017/images.019-480.jpg 480w, /images/funka2017/images.019.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="Autocomplete search menu being used"></picture>
</figure>
<blockquote>
<p>So a site’s language use can be overly tied to visual presentation. It’s also possible to use language that’s overly specific about how a site should be used. Text that attempts to aid usability for particular input types may be irrelevant or confusing to users with devices that lack that capability.</p>
</blockquote>
<blockquote>
<p>I recently encountered such an example when building an autosuggest search field. At first, I was inclined to include instructions on the field to aid in using the suggestions that it offers.</p>
</blockquote>
<blockquote>
<p>For example, a message like &quot;use your arrow keys to browse the suggestions” could be applied using the aria-describedby attribute.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.020.webp"><img src="/images/funka2017/images.020-480.jpg" srcset="/images/funka2017/images.020-480.jpg 480w, /images/funka2017/images.020.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt="A seemingly-helpful behavioral cue"></picture>
<figcaption class="offscreen">
<p>Code example:</p>
<pre><code>
&lt;input type=&quot;search&quot; aria-autocomplete=&quot;list&quot;
aria-owns=&quot;menu&quot;
aria-describedby=&quot;helptext&quot;&gt;
<p>&lt;p id=&quot;helptext&quot;&gt;Use your arrow keys to browse the suggestions&lt;/p&gt;</p>
<p>&lt;div id=&quot;menu&quot; aria-hidden=&quot;true&quot;&gt;…&lt;/div&gt;
</code></pre></p>
</figcaption>
</figure>
<blockquote>
<p>The HTML for that might look like this:</p>
</blockquote>
<blockquote>
<p>A search input with ARIA attributes noting things such as that it will offer a list autocomplete, and that it is described by a paragraph of helper text referenced elsewhere in the page (which is the text that contains my helpful note about arrow key usage)</p>
</blockquote>
<blockquote>
<p>And admittedly, there would be more code involved in making this control really function. Such as a fair amount of javascript to toggle attributes and populate the menu options as the user types.</p>
</blockquote>
<figure>
<video controls poster="/images/funka2017/images.021-480.jpg">
<source src="/images/funka2017/video-3.m4v" type="video/mp4">
</video>
<figcaption>Video clip with audio from VoiceOver</figcaption>
</figure>
<blockquote>
<p>Still, with that HTML alone, we can already focus on the text input in desktop browser, with a screenreader enabled, and hear something like this.</p>
</blockquote>
<blockquote>
<p>(audio clip: “search, edit text. Use your arrow keys to browse the suggestions.”)</p>
</blockquote>
<blockquote>
<p>To a developer like me, sitting at my work laptop, that might seem like a helpful addition. However, responsive designs need to be ready not only for many screen sizes but also different input mechanisms too, and it’s easy to forget that well-meaning instructions like these may not be helpful to folks browsing on a touchscreen for example, which often has no arrow keys to use.</p>
</blockquote>
<figure>
<video controls poster="/images/funka2017/images.022-480.jpg">
<source src="/images/funka2017/video-4.m4v" type="video/mp4">
</video>
<figcaption>Video clip with audio from iOS VoiceOver</figcaption>
</figure>
<blockquote>
<p>For someone using this control on a touchscreen and a screen reader like VoiceOver, the instructions aren’t so helpful. To support that use case, we would want to make sure the menu options are usable via tab focus, as that will map nicely to VoiceOver’s screen gestures.</p>
</blockquote>
<blockquote>
<p>As we do more and more testing to support different modes of input like that, we increase the likelihood of the control being intuitive to use without many instructions, so we can shift the helper text to describe something less input specific. In my case, I ended up changing the text to say this:</p>
</blockquote>
<blockquote>
<p>(Audio clip: ”As you type, relevant search suggestions will automatically appear after this field.&quot;)</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.023.webp"><img src="/images/funka2017/images.023-480.jpg" srcset="/images/funka2017/images.023-480.jpg 480w, /images/funka2017/images.023.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="Accessible Hiding"></picture>
</figure>
<blockquote>
<p>Another common practice is properly hiding content that should not be presented visually in a design, but that is beneficial to those using assistive tech.</p>
</blockquote>
<blockquote>
<p>Generally, I find it’s best to avoiding hiding content at all unless it improves the user experience to do so, but if you need to accessibly hide content, there are many things to keep in mind.</p>
</blockquote>
<blockquote>
<p>From a CSS standpoint, hiding HTML elements so that only screen readers can access them is both common and reliable.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.024.webp"><img src="/images/funka2017/images.024-480.jpg" srcset="/images/funka2017/images.024-480.jpg 480w, /images/funka2017/images.024.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>Code example:</p>
<pre><code>
.accessible-hidden {
position: absolute;
top: 0;
left: -999px;
height: 1px;
width: 1px;
clip: rect(1px, 1px, 1px, 1px);
overflow: hidden;
white-space: nowrap;
}
</code></pre>
</figcaption>
</figure>
<blockquote>
<p>The way that’s typically done is to position an element outside of the visual viewport so that it’s not in the rendered view, using some styles like this. As long as styles like display: none and visibility hidden are avoided, the content can still be accessed by screen readers.</p>
</blockquote>
<blockquote>
<p>There’s a lot of history packed into this little bit of code. But the basic mechanics of it simply move the element offscreen and collapse its dimensions so that it’s not visible. The 1 pixel size became common because VoiceOver would ignore text with 0px dimensions. Rules like clip are there as extra safeguards for certain elements that don’t collapse to 1px without a fight. And -999px is generally enough to get an element well outside the left of the viewport, unless you’re building a right-to-left layout…</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.025.webp"><img src="/images/funka2017/images.025-480.jpg" srcset="/images/funka2017/images.025-480.jpg 480w, /images/funka2017/images.025.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>Code example:</p>
<pre><code>
.accessible-hidden {
position: absolute;
top: 0;
left: -999px;
height: 1px;
width: 1px;
clip: rect(1px, 1px, 1px, 1px);
overflow: hidden;
<strong>white-space: nowrap;</strong>
}
</code></pre>
</figcaption>
</figure>
<blockquote>
<p>One part of this code that I find particularly interesting with regards to accessibility is the last rule.</p>
</blockquote>
<blockquote>
<p>I learned recently that when you collapse an element down to small dimensions like this, the text inside it will naturally wrap, and some screen readers may interpret those word wraps in unhelpful ways, such as ignoring them entirely so that sentences are read aloud without spaces!</p>
</blockquote>
<blockquote>
<p>To avoid that, you should include white-space: nowrap in your accessible-hidden styles, and the text doesn’t virtually wrap inside the 1px container, and the spaces will be retained.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.026.webp"><img src="/images/funka2017/images.026-480.jpg" srcset="/images/funka2017/images.026-480.jpg 480w, /images/funka2017/images.026.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>Code example:</p>
<pre><code>
.accessible-hidden:focus {
position: fixed;
top: 0;
left: 0;
background: #fff;
padding: 10px;
}
</code></pre>
</figcaption></figure>
<blockquote>
<p>Another thing that’s easy to forget is that hiding content accessibly will not prevent focusable content from receiving keyboard focus.</p>
</blockquote>
<blockquote>
<p>When the roving focus state seems to disappear while navigating the page because it ended up focusing on hidden content, it can be very disorienting.</p>
</blockquote>
<blockquote>
<p>To avoid this problem, I try to remember that any accessibly-hidden content should no longer be hidden when it gains focus. As one example, the CSS shown here will position an accessibly-hidden element element at the top of the viewport when focused</p>
</blockquote>
<figure class="emphasize">
<video controls poster="/images/funka2017/images.027-480.jpg">
<source src="/images/funka2017/video-5.m4v" type="video/mp4">
</video>
<figcaption>Skip link on this site</figcaption>
</figure>
<blockquote>
<p>And that’s a common approach for styling skip links.</p>
</blockquote>
<blockquote>
<p>We use this CSS for the skip links on the interior pages of Filament’s site, all of which have a skip-to-content link that is hidden when not focused. If you’re tabbing through this page, it’ll be the first element that gains focus, shown top/center.</p>
</blockquote>
<blockquote>
<p>This is really helpful to all keyboard users, especially those who are sighted or partially sighted, perhaps using a screen reader as an aid on top of the visual interface. In those situations especially, the parity between visual and aural experience is really critical.</p>
</blockquote>
<blockquote>
<p>So in the rare cases where it makes sense to hide focusable content, this approach is a helpful way to ensure it doesn’t stay hidden when actually focused.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.028.webp"><img src="/images/funka2017/images.028-480.jpg" srcset="/images/funka2017/images.028-480.jpg 480w, /images/funka2017/images.028.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="'Transformed' Meaning"></picture>
</figure>
<blockquote>
<p>The ways CSS presentation can unintentionally impact accessibility are scary and fascinating to me.</p>
</blockquote>
<blockquote>
<p>That text hiding gotcha was one really good example of that.</p>
</blockquote>
<blockquote>
<p>Another one I have run into comes from the common technique of using CSS text-transform to change text letter case.</p>
</blockquote>
<figure class="emphasize">
<video controls poster="/images/funka2017/images.029-480.jpg">
<source src="/images/funka2017/video-6.m4v" type="video/mp4">
</video>
<figcaption>Same button, different audio</figcaption>
</figure>
<blockquote>
<p>It’s common to see all-caps used for small buttons in an interface, and the “right” way to do this is to use ordinary sentence case text in the source code and use CSS to transform it to all-caps, and perhaps even only at certain viewport sizes.</p>
</blockquote>
<blockquote>
<p>One thing you might not expect is that VoiceOver will sometimes speak an element differently depending on whether this CSS style is applied.</p>
</blockquote>
<blockquote>
<p>Audio clip: “Add to Bag, A-D-D to Bag”</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.030.webp"><img src="/images/funka2017/images.030-480.jpg" srcset="/images/funka2017/images.030-480.jpg 480w, /images/funka2017/images.030.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>Fun with aural text-transform</p>
<ul>
<li>Time of arrival (est.) : "Time of arrival, E.S.T."</li>
<li>men : "M.E.N."</li>
<li>zip : "Z.I.P."</li>
<li>Us : "U.S."</li>
</ul>
</figcaption>
</figure>
<blockquote>
<p>So voiceover seems to assume that when some words are made all-caps, just with CSS, they become acronyms and it reads them one letter at a time. This could create some pretty confusing situations.</p>
</blockquote>
<blockquote>
<p>For example, an estimated time of arrival could become the time of arrival in the EST, the eastern time zone.</p>
</blockquote>
<blockquote>
<p>Or, perhaps a link to “Visit Us” would be read as “Visit U.S.”, which sounds like it’s suggesting you visit the United States. And that’s a very different thing.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.031.webp"><img src="/images/funka2017/images.031-480.jpg" srcset="/images/funka2017/images.031-480.jpg 480w, /images/funka2017/images.031.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>Code example:</p>
<pre><code>
&lt;button aria-label=&quot;Add to bag&quot;&gt;Add to bag&lt;/button&gt;
</code></pre>
</figcaption>
</figure>
<blockquote>
<p>Thankfully if you run into this problem yourself, there is a workaround I’ve used that can fix it.</p>
</blockquote>
<blockquote>
<p>Aria-label will let you specify the text as you’d like it to be read. It’s not ideal, but it works, and we actually had to do use this in a recent project.</p>
</blockquote>
<blockquote>
<p>In theory, the CSS property “speak: normal” (as opposed to “speak: spell-out”) should fix this too, which would be better. But I was not able to get it to work in VoiceOver.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.032.webp"><img src="/images/funka2017/images.032-480.jpg" srcset="/images/funka2017/images.032-480.jpg 480w, /images/funka2017/images.032.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt="Toggling Togglability"></picture>
</figure>
<blockquote>
<p>One of the most common challenges I find in building responsive layouts comes from accurately communicating the role and state of responsive toggles.</p>
</blockquote>
<blockquote>
<p>So much of UI design involves toggles, whether they’re accordions, menus that open and close, dialogs, tabs.</p>
</blockquote>
<blockquote>
<p>But toggles that are only sometimes user-interactive, depending on various conditions, can be particularly tricky to communicate well to assistive technology.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.033.webp"><img src="/images/funka2017/images.033-480.jpg" srcset="/images/funka2017/images.033-480.jpg 480w, /images/funka2017/images.033.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt=""></picture>
<figcaption>Image with navigation either collapsible or static, based on viewport size</figcaption>
</figure>
<blockquote>
<p>Consider a component like this one.</p>
</blockquote>
<blockquote>
<p>On small screens like the phone at the top, we have some navigation lists that are user-expandable, while on wider screens the list items are shown at all times, without any ability for the user to collapse them, and the buttons that formerly acted as toggles are instead just text headings above each list.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.034.webp"><img src="/images/funka2017/images.034-480.jpg" srcset="/images/funka2017/images.034-480.jpg 480w, /images/funka2017/images.034.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption>Another example of navigation that's collapsible or static, based on viewport size</figcaption>
</figure>
<blockquote>
<p>Here’s another example of this same sort of behavior, from the navigation on Filament’s website.</p>
</blockquote>
<blockquote>
<p>Again, here we have a list of navigation links, sometimes preceded by a button that lets you toggle their visibility, or sometimes displayed statically without any need for user interaction.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.035.webp"><img src="/images/funka2017/images.035-480.jpg" srcset="/images/funka2017/images.035-480.jpg 480w, /images/funka2017/images.035.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt="Quote from Dave Rupert: I'm trying to show/hide less. It's trivial to show/hide with media queries, but managing state + viewport in ARIA is a PITA."></picture>
<figcaption><a href="https://twitter.com/davatron5000/status/821755038171332609">Tweet source</a></figcaption>
</figure>
<blockquote>
<p>In these two examples, the components are different enough in behavior that they should be described differently to assistive technology.</p>
</blockquote>
<blockquote>
<p>I find it’s fairly easy to find examples online for creating either a variant of this component’s HTML in accessible ways. But managing both in the same UI is less often discussed.</p>
</blockquote>
<blockquote>
<p>As a result, developers like Dave Rupert here have admitted that they’re trying to show and hide content less often, just to avoid dealing with the complexity of doing it well…</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.036.webp"><img src="/images/funka2017/images.036-480.jpg" srcset="/images/funka2017/images.036-480.jpg 480w, /images/funka2017/images.036.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>HTML Starting Point</p>
<pre><code>
&lt;h2 class=&quot;collapsible_header&quot;&gt;Packing List&lt;/h2&gt;
<pre><code>&amp;lt;div class=&amp;quot;collapsible_content&amp;quot;&amp;gt;
...
&amp;lt;/div&amp;gt;
</code></pre>
<p></code></pre></p>
</figcaption>
</figure>
<blockquote>
<p>So how might we do it well?</p>
</blockquote>
<blockquote>
<p>Well, here’s how that component might start out in the HTML source, before any JavaScript is applied to make it user-toggleable.</p>
</blockquote>
<blockquote>
<p>Pretty basic. There’s a level 2 heading, followed by a div containing some collapsible content, perhaps a list of links.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.037.webp"><img src="/images/funka2017/images.037-480.jpg" srcset="/images/funka2017/images.037-480.jpg 480w, /images/funka2017/images.037.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption><a href="https://www.w3.org/TR/aria-in-html/#second">Screenshot from: Second Rule of ARIA use</a></figcaption>
</figure>
<blockquote>
<p>Now, to make that markup meaningful as a toggle control, it needs some additions.</p>
</blockquote>
<blockquote>
<p>If you’re like me, your first inclination might be to use JavaScript to transform the role of that heading into a button, but the ARIA specification’s rule number 2 explicitly asks that we do not do that.</p>
</blockquote>
<blockquote>
<p>Instead, we should insert a button element into the heading, which allows that heading to stay in the document tree as handy accessible navigation.</p>
</blockquote>
<blockquote>
<p>I should note here that the ARIA spec has some fantastic documentation nowadays and this particular page has a short list of rules that are really great as high-level guides.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.038.webp"><img src="/images/funka2017/images.038-480.jpg" srcset="/images/funka2017/images.038-480.jpg 480w, /images/funka2017/images.038.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>An Interactive Toggle</p>
<pre><code>
&lt;h2 class=&quot;collapsible_header&quot;&gt;
&lt;button aria-haspopup=&quot;true&quot; aria-controls=&quot;packinglist&quot; aria-expanded=&quot;false&quot;&gt;Packing List&lt;/button&gt;
&lt;/h2&gt;
&lt;div class=&quot;collapsible_content&quot; role=&quot;menu&quot; aria-hidden=&quot;true&quot; id=&quot;packinglist&quot;&gt;
...
&lt;/div&gt;
</code></pre>
</figcaption>
</figure>
<blockquote>
<p>So for a clickable toggle, the heading should have a focusable button element inside it.</p>
</blockquote>
<blockquote>
<p>There are also some ARIA attributes we can add to communicate the state (expanded or collapsed) and the relationship between the button and content.</p>
</blockquote>
<blockquote>
<p>Typically, we would add this markup with JavaScript, since the collapsible needs JavaScript to be functional.</p>
</blockquote>
<blockquote>
<p>But we also want to selectively add or remove this markup depending on the viewport size and which version of our component is in play.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.039.webp"><img src="/images/funka2017/images.039-480.jpg" srcset="/images/funka2017/images.039-480.jpg 480w, /images/funka2017/images.039.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>Pairing Behavior with Presentation</p>
<pre><code>
var big = window.matchMedia( &quot;(min-width: 800px)&quot; ).matches;
if( big ){
removeToggle();
}
else {
addToggle();
}
</code></pre>
</figcaption>
</figure>
<blockquote>
<p>And that part is tricky. Changing markup across responsive layouts is not as easy as changing presentation. We have to do it by adding or removing attributes and elements. And we need to figure out WHEN to do that as well.</p>
</blockquote>
<blockquote>
<p>One way we could figure that out is to use the matchMedia function, which lets us test media queries in JavaScript to see if they match or not.</p>
</blockquote>
<blockquote>
<p>We might check if a media query matches when the page loads, and listen for it to change at later times too…</p>
</blockquote>
<blockquote>
<p>And this would work, but it also adds some unfortunate maintenance overhead, as now we have presentation information in our JavaScript as well as our CSS.</p>
</blockquote>
<figure class="emphasize">
<video controls poster="/images/funka2017/images.040-480.jpg">
<source src="/images/funka2017/video-7.m4v" type="video/mp4">
</video>
<figcaption>Video showing a toggle's visual affordance via mouse cursor type</figcaption>
</figure>
<blockquote>
<p>A better way might be to write our script to inspect which CSS rules are active, and based on that, we can know which version of the component is being shown. Then we can update the HTML to best communicate that version.</p>
</blockquote>
<blockquote>
<p>To do this, I’ve used the CSS cursor property (which changes the mouse cursor) to denote whether the heading is supposed to act as a toggle at a particular breakpoint. For example, the cursor should display as a “pointer” when the toggle is interactive, and as the “default” arrow when it is not.</p>
</blockquote>
<blockquote>
<p>With the cursor property denoting the version of the component, my JavaScript could check this property and manipulate the HTML and behavior accordingly.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.041.webp"><img src="/images/funka2017/images.041-480.jpg" srcset="/images/funka2017/images.041-480.jpg 480w, /images/funka2017/images.041.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>Toggle or not, in CSS<p>
<pre><code>
.collapsible_header {
cursor: pointer;
}
@media (min-width: 800px){
.collapsible_header {
cursor: default;
}
}
</code></pre>
</figcaption>
</figure>
<blockquote>
<p>To recap that last bit, here is how that looks in CSS.</p>
</blockquote>
<blockquote>
<p>Our collapsible heading begins with a cursor of pointer. Then at 800px the cursor property changes to default, declaring the heading non-interactive.</p>
</blockquote>
<blockquote>
<p>JavaScript can read active CSS rules so it pick up on this and manipulate the markup accordingly, removing the ARIA attributes and button.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.042.webp"><img src="/images/funka2017/images.042-480.jpg" srcset="/images/funka2017/images.042-480.jpg 480w, /images/funka2017/images.042.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>Checking Presentation<p>
<pre><code>
var headingCSS = getComputedStyle( myHeading, null );
if( headingCSS.getPropertyValue( "cursor" ) === "pointer" ){
addToggle();
}
else {
removeToggle();
}
</code></pre>
</figcaption>
</figure>
<blockquote>
<p>Here’s an example of some JavaScript that is checking the cursor CSS property.</p>
</blockquote>
<blockquote>
<p>Technically, the getComputedStyle method allows us to do this, so I’m using that to get the styles of myHeading, which represents my collapsible heading.</p>
</blockquote>
<blockquote>
<p>Based on whether the value is pointer, I can make decisions on modifying the markup to match the version of the component that is in play.</p>
</blockquote>
<blockquote>
<p>So this is a little better than the matchMedia version, because now I can have many responsive toggles using the same logic, and the JavaScript doesn’t need to know the breakpoints at which they change from toggles to static.</p>
</blockquote>
<figure class="emphasize">
<video controls poster="/images/funka2017/images.043-480.jpg">
<source src="/images/funka2017/video-8.m4v" type="video/mp4">
</video>
<figcaption>Toggle demo with sound from VoiceOver screenreader</figcaption>
</figure>
<blockquote>
<p>Here’s how this component sounds in VoiceOver, the built in Mac screen reader, at its various sizes.</p>
</blockquote>
<blockquote>
<p>First as a collapsible menu, then as a simple navigation list with no menu button</p>
</blockquote>
<blockquote>
<p>So we can see that it pairs well with the UI at both breakpoints.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.044.webp"><img src="/images/funka2017/images.044-480.jpg" srcset="/images/funka2017/images.044-480.jpg 480w, /images/funka2017/images.044.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption>Screenshot of a dialog overlay that becomes static content at wider viewport sizes</figcaption>
</figure>
<blockquote>
<p>A similar challenge can occur with modal dialogs. Sometimes we use modals on small screens to toggle the display of content that is shown at all times on wider screen sizes.</p>
</blockquote>
<blockquote>
<p>Take for example, this Filters panel for a search results page.</p>
</blockquote>
<blockquote>
<p>On small screens, a user can open the filters in a modal dialog, and this is convenient because you don’t have to scroll back and forth on a small screen to refine your search.</p>
</blockquote>
<blockquote>
<p>On wider screens however, this content sits statically on the left side of the results, because there’s more room to display it.</p>
</blockquote>
<figure class="emphasize">
<video controls poster="/images/funka2017/images.045-480.jpg">
<source src="/images/funka2017/video-9.m4v" type="video/mp4">
</video>
<figcaption>Video of a dialog control that becomes static, inline content</figcaption>
</figure>
<blockquote>
<p>Here’s a more generic version of a dialog control that does this same thing. This is an open dialog. And you can see its presentation change across breakpoints</p>
</blockquote>
<blockquote>
<p>Similarly to the collapsible toggle, these two states should be communicated differently in HTML.</p>
</blockquote>
<blockquote>
<p>Thankfully I found that we could use JavaScript to monitor and toggle the relevant attributes for the dialog, just like we did with the collapsible toggle.</p>
</blockquote>
<blockquote>
<p>With a dialog of course, the markup is a little different.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.046.webp"><img src="/images/funka2017/images.046-480.jpg" srcset="/images/funka2017/images.046-480.jpg 480w, /images/funka2017/images.046.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>Static Content starting point...<p>
<pre><code>
&lt;div class=&quot;dialog&quot; id=&quot;mydialog&quot;&gt;
&#x9;&lt;p&gt;This is some content.&lt;/p&gt;
&lt;/div&gt;
</code></pre>
</figcaption>
</figure>
<blockquote>
<p>The static, initial markup for potentially-modal content like this is nothing fancy.</p>
</blockquote>
<blockquote>
<p>Here’s a div with some content inside it.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.047.webp"><img src="/images/funka2017/images.047-480.jpg" srcset="/images/funka2017/images.047-480.jpg 480w, /images/funka2017/images.047.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>Static Content in a dynamic modal<p>
<pre><code>
&lt;div class=&quot;dialog&quot; id=&quot;mydialog&quot; aria-label=&quot;dialog example&rdquo; role=&quot;dialog&quot; tabindex=&quot;0&quot;&gt;
&#x9;&lt;p&gt;This is some content.&lt;/p&gt;
&lt;button&gt;Close dialog&lt;/button&gt;
&lt;/div&gt;
</code></pre>
</figcaption>
</figure>
<blockquote>
<p>However, when transformed into a functional dialog with JavaScript, that HTML should look more like this.</p>
</blockquote>
<blockquote>
<p>I’ve added a role of dialog, a tabindex attribute to allow focus, an aria-label attribute to describe the dialog, and a button to close the dialog.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.048.webp"><img src="/images/funka2017/images.048-480.jpg" srcset="/images/funka2017/images.048-480.jpg 480w, /images/funka2017/images.048.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>Modal or not, in CSS<p>
<pre><code>
.dialog { display: none; }
.dialog-open { display: block; }
@media (min-width: 800px){
.dialog {
display: block;
}
.dialog button {
display: none;
}
}
</code></pre>
</figcaption>
</figure>
<blockquote>
<p>The CSS for toggling between the two versions of this component looks like this. We start by the dialog-open class is present.</p>
</blockquote>
<blockquote>
<p>At 800px, we shift the display property to block even when the open class is not present, making it visible at all times.</p>
</blockquote>
<blockquote>
<p>We also set the close button to display: none.</p>
</blockquote>
<blockquote>
<p>With this CSS in place, we can write JavaScript to pick up on the presentation and manipulate the markup, depending on the breakpoint. We’d want the script to check if the button is both display none AND the dialog is visible. If both of those are true, it’s a static dialog, and the script removes the modal-related attributes.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.049.webp"><img src="/images/funka2017/images.049-480.jpg" srcset="/images/funka2017/images.049-480.jpg 480w, /images/funka2017/images.049.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt=""></picture>
<figcaption><a href="https://www.lullabot.com/articles/importing-css-breakpoints-into-javascript">Source article</a></figcaption>
</figure>
<blockquote>
<p>So, the approach of detecting CSS properties has worked for me in the past, but it still involves a degree of awareness of the CSS styles that drive each presentation.</p>
</blockquote>
<blockquote>
<p>Fortunately, there are some cleaner ways to communicate information from CSS to JavaScript that you might consider.</p>
</blockquote>
<blockquote>
<p>Mike Herchel’s article Importing CSS Breakpoints into JavaScript, from 2015, covers one such method.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.050.webp"><img src="/images/funka2017/images.050-480.jpg" srcset="/images/funka2017/images.050-480.jpg 480w, /images/funka2017/images.050.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>Hacking the content property<p>
<pre><code>
body:before {
content: "smartphone";
display: none; /* Prevent from displaying. */
}
@media (min-width: 800px) {
body:before {
content: "tablet";
}
}
</code></pre>
</figcaption>
</figure>
<blockquote>
<p>In the article, Mike uses CSS generated content, otherwise known as the :before or :after selector, which is a CSS selector that inserts a text element into the page.</p>
</blockquote>
<blockquote>
<p>And he sets the content property of that generated element to different values, which JavaScript can read and use in its logic.</p>
</blockquote>
<blockquote>
<p>He uses terms like smartphone, tablet, but we could replace that for something more component-minded like collapsed or expanded, or modal or static.</p>
</blockquote>
<blockquote>
<p>Neat. The downside to it is minor, but worth noting: generated content is not designed for this purpose, so it’s a bit of a hack. Be sure to use display none so it doesn’t appear in the design.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.051.webp"><img src="/images/funka2017/images.051-480.jpg" srcset="/images/funka2017/images.051-480.jpg 480w, /images/funka2017/images.051.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt=""></picture>
<figcaption><a href="https://justmarkup.com/log/2017/01/handling-states-on-resize-using-css-custom-properties/">Source Article</a></figcaption>
</figure>
<blockquote>
<p>In Michael Scarnagl’s article, “Handling states on resize using CSS custom properties,” I found a cleaner and more forward looking approach.</p>
</blockquote>
<blockquote>
<p>In it, Michael describes how to use CSS custom properties to communicate state to JavaScript.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.052.webp"><img src="/images/funka2017/images.052-480.jpg" srcset="/images/funka2017/images.052-480.jpg 480w, /images/funka2017/images.052.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption class="offscreen">
<p>Custom CSS Properties, instead<p>
<pre><code>
.js--expandable {
--expandable: true;
}
@media (min-width: 800px) {
.js--expandable {
--expandable: false;
}
}
</code></pre>
</figcaption>
</figure>
<blockquote>
<p>Is anyone here using CSS custom properties yet?</p>
</blockquote>
<blockquote>
<p>Well, if custom properties are new to you, well, they honestly were to me too. They’re a pretty new standard that don’t yet enjoy full modern browser support.</p>
</blockquote>
<blockquote>
<p>But Michael’s concept here is neat.</p>
</blockquote>
<blockquote>
<p>He’s using a custom property “—expandable”, which JavaScript can detect like any other CSS property.</p>
</blockquote>
<blockquote>
<p>Best of all, custom properties have no native presentation to worry about, so they can be used without worry of interference in the UI.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.053.webp"><img src="/images/funka2017/images.053-480.jpg" srcset="/images/funka2017/images.053-480.jpg 480w, /images/funka2017/images.053.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt="Maintaining Focus"></picture>
</figure>
<blockquote>
<p>The last issue I wanted to discuss is one I that think still has no great solutions, yet it is going to become more and more of a problem for users across the continuum of use cases</p>
</blockquote>
<blockquote>
<p>This issue is not even new. In fact it predates responsive design, though responsive design can compound the problem for certain.</p>
</blockquote>
<blockquote>
<p>It arrises when we use CSS to reorder the display of content so that it no longer follows the order in the page source.</p>
</blockquote>
<blockquote>
<p>With CSS, it’s trivial to do this, and most complex layouts, responsive or not, end up moving portions of HTML out of their natural source order at least in small amounts.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.054.webp"><img src="/images/funka2017/images.054-480.jpg" srcset="/images/funka2017/images.054-480.jpg 480w, /images/funka2017/images.054.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt="Screenshot of this website at small and wide viewport sizes"></picture>
</figure>
<blockquote>
<p>For example, just float some focusable elements to the right and you’ve done it: tab order will start on the right, then move left, opposite of display. Screen readers tend to follow source order in this way too, so elements would be read content from right to left in that case.</p>
</blockquote>
<blockquote>
<p>Here’s a vertical example from Filament’s homepage. Sighted users on small screens see our logo &amp; description first. Yet wider screens get our navigation first.</p>
</blockquote>
<blockquote>
<p>In both, the HTML is the same, but the small screen layout follows source order. Meaning that the large screen experience has a disconnect between keyboard navigation and display. And yet, from a design perspective, we feel each presentation suits its context quite well.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.055.webp"><img src="/images/funka2017/images.055-480.jpg" srcset="/images/funka2017/images.055-480.jpg 480w, /images/funka2017/images.055.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt=""></picture>
<figcaption><a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/">Flex direction example source</a></figcaption>
</figure>
<blockquote>
<p>I expect this problem to get worse in the near future, as new CSS features like Flexbox &amp; Grid have now landed in most browsers and make it even easier to depart from source order to achieve complex layouts.</p>
</blockquote>
<blockquote>
<p>Take flex box’s flex-direction: row-reverse value, which will flip the order of child elements from left to right to right to left. You can do the same vertically with column-reverse.</p>
</blockquote>
<blockquote>
<p>This makes for a dramatic departure between HTML and how it’s presented on the screen.</p>
</blockquote>
<blockquote>
<p>But is this not also the promise of CSS? Freeing layout from the constraints of source order is what drove web standards adoption away from table layouts. It’s hard to say we should not use CSS for what it’s designed to do.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.056.webp"><img src="/images/funka2017/images.056-480.jpg" srcset="/images/funka2017/images.056-480.jpg 480w, /images/funka2017/images.056.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt="Quote from Leonie Watson: …The only viable way (in my opinion) for the flexbox disconnect to be resolved, is in the browser"></picture>
<figcaption><a href="http://tink.uk/flexbox-the-keyboard-navigation-disconnect/">Article source</a></figcaption>
</figure>
<blockquote>
<p>Some folks think that browsers should step up to address this issue, perhaps by forcing tab and reading order to follow the visual display.</p>
</blockquote>
<blockquote>
<p>In my mind, that would be preferable to asking developers to either try to number elements tab index attributes manually, or worse, asking them to not use new CSS layout features.</p>
</blockquote>
<blockquote>
<p>Leonie Watson has written about this and thinks vendors need to address it. Indeed, as she notes, Firefox formerly had a bug in its Flexbox implementation that made tab focus follow visual order. They’ve since fixed it, but as she notes, maybe it was a good idea.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.057.webp"><img src="/images/funka2017/images.057-480.jpg" srcset="/images/funka2017/images.057-480.jpg 480w, /images/funka2017/images.057.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt="screenshot of a form with two columns of fields, each meant to be navigated top to bottom"></picture>
</figure>
<blockquote>
<p>I think it would be great if browsers addressed this too, but I’ll admit it’s not without nuance.</p>
</blockquote>
<blockquote>
<p>For example, in a form layout like this one, it make sense to navigate from the first two inputs to the fields beneath them.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.058.webp"><img src="/images/funka2017/images.058-480.jpg" srcset="/images/funka2017/images.058-480.jpg 480w, /images/funka2017/images.058.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt="Same image as last, with a right column form field highlighted as receiving focus out of intended order"></picture>
</figure>
<blockquote>
<p>But a strict and not-very-smart calculated left-to-right focus order might end up roving focus to the next field on the right, which would not follow the expected logical order of the form.</p>
</blockquote>
<blockquote>
<p>Alternatively, there are surely many examples like this where you would want focus to rove to elements that are positioned in a particular visual spot, yet are greatly separated in source order, so it goes both ways.</p>
</blockquote>
<blockquote>
<p>Perhaps fieldsets and other semantic markup can aid the browser in making these decisions, but I’m not sure. It’s a complicated problem.</p>
</blockquote>
<figure class="emphasize"><picture><source type="image/webp" srcset="/images/funka2017/images.059.webp"><img src="/images/funka2017/images.059-480.jpg" srcset="/images/funka2017/images.059-480.jpg 480w, /images/funka2017/images.059.jpg 1280w" sizes="(min-width: 690px) 690px, 100vw" alt=""></picture>
<figcaption><a href="https://www.w3.org/WAI/GL/wiki/Using_ARIA_landmarks_to_identify_regions_of_a_page">W3C Information on ARIA Landmark Roles</a></figcaption>
</figure>
<blockquote>
<p>Thankfully it is being worked out as we speak, and hopefully we’ll see some improvements soon from browser vendors.</p>
</blockquote>
<blockquote>
<p>For now, at the very least, we should be using landmark roles or HTML 5 elements to communicate broad structural landmarks in our UI.</p>
</blockquote>
<blockquote>
<p>Landmark roles like header, main, nav, and contentinfo help a great deal when navigating a document with assistive technology, regardless of presentation, so keep using them to mitigate source order issues.</p>
</blockquote>
<figure><picture><source type="image/webp" srcset="/images/funka2017/images.060.webp"><img src="/images/funka2017/images.060-480.jpg" srcset="/images/funka2017/images.060-480.jpg 480w, /images/funka2017/images.060.jpg 1280w" sizes="(min-width: 690px) 288px, 100vw" alt="Building for access is our job"></picture>
</figure>
<blockquote>
<p>So I’ve covered a lot about complications at the intersection of responsive design and accessibility.</p>
</blockquote>
<blockquote>
<p>There’s much, much more to think about here, and I hope to see more discussion in this area as folks continue to improve the accessibility of their responsive sites.</p>
</blockquote>
<blockquote>
<p>We need to remember that we can build beautiful,complex sites that are broadly accessible.</p>
</blockquote>
<blockquote>
<p>Its harder to do that. It’s harder to deliver responsibly, and resiliently.</p>
</blockquote>
<blockquote>
<p>But… it’s important, and it’s one of the most satisfying parts of our job.</p>
</blockquote>
Layersnap! Markup-driven SVG Animations2017-02-02T19:00:00-05:00<h1>LayerSnap! Markup-based SVG Animations</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> on 02/03/2017</p>
<p><a href="http://master.origin.layersnap.fgview.com/demo/"><img src="/images/layersnap-branding.png" alt="&quot;Layersnap logo branding screenshot&quot;"></a></p>
<p>Every so often we come across parts of our designs that could really benefit from a smooth build animation or transition. Fortunately, we use a lot of SVG for delivering our vector graphics, so we have a great deal of animation flexibility at our disposal, and one tool we’ve enjoyed using for helping us animate our SVGs is <a href="http://snapsvg.io/">SnapSVG</a>, by Adobe. Snap is great, but takes a decent amount of JavaScript skill and tinkering to churn out an animation.</p>
<p>While working on some projects last year, we realized that if we had a more user-friendly, markup-based approach to configuring our SVG animations, we’d be more productive, and our clients could easily work with their own animations after hand-off as well. So we built an open-source tool!</p>
<h2 id="introducing-layersnap">Introducing LayerSnap <a class="direct-link" href="#introducing-layersnap" aria-hidden="true">#</a></h2>
<p><a href="http://master.origin.layersnap.fgview.com/demo/">LayerSnap</a> is a script that builds on top of Snap SVG to build simple SVG animations by editing the SVG markup’s attributes. It comes with a suite of pre-set build transitions like “fade”, “move-down”, “enter-left”, “rotate-right”, etc. It also has an “anvil” transition, because no animation toolkit is complete without an anvil transition. These transitions can be configured via an attribute on every group (<code>g</code>) element in an SVG, and a special syntax allows you to combine each transition with a host of configuration settings for their speed, delay, easing, looping, repeating, and even interactivity.</p>
<h2 id="demos%2C-docs%2C-and-more">Demos, Docs, and more <a class="direct-link" href="#demos%2C-docs%2C-and-more" aria-hidden="true">#</a></h2>
<ul>
<li>To get started with demos and documentation, check out the <a href="https://master-origin-layersnap.fgview.com/demo/">Layersnap project homepage</a></li>
<li>The source code is on <a href="https://github.com/filamentgroup/layersnap">Github</a> and <a href="https://www.npmjs.com/package/layersnap">NPM</a></li>
<li>To file an issue for us to look at, or help us with a bug fix, please visit <a href="https://github.com/filamentgroup/layersnap/issues">the issue tracker</a>.</li>
</ul>
<p>This is our initial release of LayerSnap, so we expect it’ll have some issues here and there to work out. We’d love your help in testing it out. LayerSnap has a couple of dependencies (a CSS file and Snap SVG’s JavaScript), but you’ll notice our project demos include jQuery and some other utilities for cueing animations when they enter the viewport as you scroll. jQuery isn’t required by LayerSnap, but if you’re already using it in your project, you’ll find a helper script or two in the LayerSnap repo makes it easier to auto-run your animations.</p>
<p>Have any thoughts on LayerSnap? Please let us know <a href="https://twitter.com/filamentgroup">over on twitter</a>! Enjoy!</p>
Maintaining Accessibility in a Responsive World2017-02-01T19:00:00-05:00<h1>Maintaining Accessibility in a Responsive World</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> on 02/02/2017</p>
<p>At Filament Group, our primary concern has long been to build sites that are accessible to as many people as possible. In that aim, being “accessible” <a href="https://the-pastry-box-project.net/anne-gibson/2014-july-31">means many things</a>. It means that a page must load quickly—even in slow and spotty mobile networks. After loading, the page should be usable and feel appropriate and intuitive to folks using any device, browser, viewport size, and assistive technology. More often than not, the practices we use to achieve these goals play nicely together, but sometimes one optimization can complicate another. When that happens, we try to step back and find ways to satisfy all our priorities.</p>
<h2 id="disconnected-interfaces">Disconnected interfaces <a class="direct-link" href="#disconnected-interfaces" aria-hidden="true">#</a></h2>
<p>One such complication we’ve encountered is in accurately communicating to assistive technology (e.g. screen readers) the hierarchy and state of a responsive layout. CSS media queries enable us to adapt the way our HTML is presented visually depending on viewport size, but doing that can result in a disconnect between the markup that keyboard and assistive technology users interact with, and the way that markup is being presented on the screen.</p>
<p>That disconnect can create usability barriers for many people. Our field typically discusses screen reader usage as it relates to blind users, and those are certainly common and critical use cases. But it’s easy to forget that folks use screen readers and other assistive tech (AT) for <a href="http://a11yproject.com/posts/myth-accessibility-is-blind-people/">a far wider variety of additional reasons</a>, from partial sightedness to cognitive disabilities, to all kinds of physical impairment (temporary and not).</p>
<h2 id="identifying-the-particulars">Identifying the particulars <a class="direct-link" href="#identifying-the-particulars" aria-hidden="true">#</a></h2>
<p>Some related issues surfaced for us during a recent accessibility audit of a large-scale site we’d built. Initial rounds of screen reader testing seemed to go well, and that pleased us as we’d taken the time to use semantic, well-organized markup and even use ARIA attributes when necessary. In later rounds of testing though, sighted testers used a widescreen display in combination with a screen reader and ran into some problems:</p>
<ul>
<li><a href="#cues">Spatial &amp; Behavioral Cues</a>: cues in our markup that were designed to aid usability were inapplicable in some usage contexts (“navigate using your arrow keys”) or viewport sizes (“the example <em>above</em> demonstrates…”)</li>
<li><a href="#hiding">Accessible hiding</a>: Visibly hidden content could sometimes contain focusable elements (e.g. anchors) that were still hidden when they received focus.</li>
<li><a href="#toggles">Content Toggles</a>: Some collapsible content toggles would communicate a collapsed state to AT, even when at some viewport sizes their content was statically displayed in the visual layout. Similarly, some modal dialog content was displayed inline in the page at wider CSS breakpoints, yet the ARIA role and state attributes on the markup still conveyed that it was a hidden dialog, rendering it invisible to AT.</li>
<li><a href="#focus">Focus Order</a>: The order in which elements received focus while tabbing through the page was sometimes inconsistent with the order those elements were visually displayed.</li>
</ul>
<p>These issues presented a need to better communicate the role and state of the interface at every breakpoint. In some cases, we were able to find maintainable solutions, while sometimes we had to settle on a pragmatic compromise. Here’s where we landed.</p>
<h3 id="cues">Spatial & Behavioral Cues</h3>
<p>The issues that related to spatial and behavioral cues were quickest to fix. In each case, we strived to find a useful way to describe how to use a component without describing too much of the particulars of the visual UI or how it should be used by any particular input mode.</p>
<p>Some examples:</p>
<ul>
<li>For a text caption that might appear below or to the right of an image depending on the viewport, we’d avoid referencing the image with terms like “above” or “left” and instead say something like “Pictured: …”</li>
<li>For an autosuggest search field that said, “use your arrow keys to browse the suggestions,” we would instead say, “As you type, relevant search suggestions will automatically appear after this field.” And then, we took additional care to make sure that the control was intuitive to use with many common means of navigation (arrow keys, tab key, etc.) so that usage instructions weren’t necessary anyway.</li>
</ul>
<h3 id="hiding">Accessible hiding</h3>
<p>The practice of <a href="https://www.w3.org/WAI/tutorials/fundamentals/hiding/">“accessible” hiding</a> aims to hide visually-unnecessary portions of page content from sighted users while retaining their visibility to assistive tech. One common way to do that is to hide an element outside of the visual viewport <em>without</em> using styles like <code>display: none</code>, which would make the content invisible to all users (AT too!). For example:</p>
<pre><code>.accessible-hidden {
position: absolute;
top: 0;
left: -999px;
height: 1px;
width: 1px;
clip: rect(1px, 1px, 1px, 1px);
white-space: nowrap;
}
</code></pre>
<p>One thing that’s easy to forget is that hiding content in this way does not prevent focusable content from receiving keyboard focus, and when the roving focus state seems to disappear off the page it can be very disorienting. To avoid this, we try to remember that any accessibly-hidden content should be visible when it gains focus. For example, this CSS would place a focused element at the top of the viewport:</p>
<pre><code>.accessible-hidden:focus {
position: fixed;
top: 0;
left: 0;
background: #fff;
padding: 10px;
etc etc...
}
</code></pre>
<p>One example of this behavior can be found on the interior pages of Filament’s site, all of which have a skip-to-content link that is hidden when not focused. If you’re tabbing through this page, it’ll be the first element that gains focus, shown top/center. Here’s how it looks:</p>
<p><img src="/images/a11ypost-skiplink.png" alt="&quot;Focused state of our accessible skip link&quot;"></p>
<p>I should note that this pattern of showing hidden content when focused is <a href="http://codepen.io/joe-watkins/pen/rjhiK">not our idea</a>, but it’s a good technique to remember. Generally, we recommend trying to avoid hiding focusable elements from any users, but we use this approach in cases where it makes sense.</p>
<h3 id="toggles">Content Toggles</h3>
<p>A larger challenge for us came in accurately communicating the role and state of responsive content toggles. Consider a component like this one, where on small screens, our navigation is only seen after tapping on a button to expand it:</p>
<p><img src="/images/a11ypost-collapse-exp.png" alt="&quot;Collapsible Navigation toggle&quot;"></p>
<p>…yet in wider viewports, the navigation is automatically expanded and the button is no longer needed:</p>
<p><img src="/images/a11ypost-collapse-lrg.png" alt="&quot;Always-expanded Navigation toggle&quot;"></p>
<p>While the difference in these two presentations should be intuitive to sighted users, the markup that we were using to describe this control only adequately described the interactive, collapsible version of the component. This meant that screen reader users with larger displays would end up with an audible experience that didn’t match the presentation on the screen. To address this, we knew we had to do more to sync the component’s behavior with its markup.</p>
<h4>Coding a “sometimes-collapsible”</h4>
<p>When a collapsible toggle like this is in its user-interactive mode, we can toggle the its content using CSS <code>display: none</code> or <code>display: block</code>. By pairing that with some JavaScript, we can enable the user to toggle that style when they click the toggle button. In our case, the toggle “button” starts out as an HTML heading element (<code>h2</code>), so that it makes sense in the page hierarchy before it has toggle behavior. Once the JavaScript behavior is applied however, we need to convey to assistive tech that the heading is now offering the toggle button behavior, so it needs some different markup in that situation. Here’s the HTML implementation we ended up using for the interactive version of the component:</p>
<pre><code>
&lt;h2 class="collapsible_header"&gt;
&lt;button aria-haspopup=&quot;true&quot; aria-controls=&quot;packinglist&quot; aria-expanded=&quot;false&quot;&gt;Packing List
&lt;/button&gt;
&lt;/h2&gt;
&lt;div class="collapsible_content" role=&quot;menu&quot; aria-hidden=&quot;true&quot; id=&quot;packinglist&quot;&gt;
...
&lt;/div&gt;
</code></pre>
<p>…and when toggled open:</p>
<pre><code>
&lt;h2 class=&quot;collapsible_header collapsible-expanded&quot;&gt;
&lt;button aria-haspopup=&quot;true&quot; aria-controls=&quot;packinglist&quot; aria-expanded=&quot;true&quot;&gt;Packing List
&lt;/button&gt;
&lt;/h2&gt;
&lt;div class="collapsible_content" role=&quot;menu&quot; aria-hidden=&quot;false&quot; id=&quot;packinglist&quot;&gt;
...
&lt;/div&gt;
</code></pre>
<p><em><strong>Note:</strong> This implementation can vary and it could be simpler for some cases. We found this configuration to test well in our audit, particularly because we were using this toggle pattern both for simple one-off toggles and for more complicated nested navigation menus. Also, we had originally wanted to change the <code>h2</code> itself into a button by adding <code>role=&quot;button&quot;</code>, thinking that it had little value as a heading once it becomes interactive, but <a href="https://www.w3.org/TR/2015/WD-aria-in-html-20150521/#second-rule-of-aria-use">ARIA rule #2</a> states we shouldn’t do that, so we added a child button and we still feel conflicted about it.</em></p>
<p>So that HTML makes the interactive toggle meaningful to assistive tech. But at viewport sizes where the collapsible content is always-displayed and not user-interactive, we’ll want to either hide the <code>h2</code> from everyone, or remove the <code>button</code> element and keep the <code>h2</code> as a regular static heading (in a typical site, we’ll often have several components that use each of these behaviors). Also at that wider viewport size, we will need to ensure that the content div is visible to all users, since there’s no longer any toggle functionality. To do that, we need to use JavaScript to remove and apply some attributes as they are needed.</p>
<p>One way to apply that JavaScript would be to look at the viewport’s size and manipulate the HTML attributes depending on the conditions that applied to the layout of each component. Of course, we knew we wanted to keep information about particular breakpoint sizes out of our JavaScript (and solely in the CSS), so we decided to set up our JavaScript to keep track of CSS properties that we knew would imply that the collapsible was interactive or not. On our collapsible header, we explicitly set the CSS <code>cursor</code> property to either <code>pointer</code> or <code>default</code>, depending on whether it’s interactive or not, respectively.</p>
<p>In code, here’s the CSS for that:</p>
<pre><code>/* interactive heading/button */
.collapsible_header {
cursor: pointer;
}
/* hide the content div if it comes after a collapsed heading */
.collapsible_content {
display: none;
}
.collapsible-expanded + .collapsible_content {
display: block;
}
@media (min-width: 700px){
/* change cursor on heading/button to default, designating it non-interactive */
.collapsible_header {
cursor: default;
/* optional: hide the heading with display: none; here */
}
.collapsible_content {
display: block;
}
}
</code></pre>
<p>And in our JavaScript, we used a <code>resize</code> handler to run a function that checks this CSS property’s value. If the value was <code>pointer</code>, we knew the CSS was currently presenting the toggle as user-interactive, and it needed to have the appropriate attributes paired with that behavior. If the value was <code>default</code>, the script would deem it to be non-interactive and the attributes would need to be removed. Here’s how that would look in JavaScript (pseudocode for example purposes):</p>
<pre><code>window.addEventListener( &quot;resize&quot;, function(){
// assume for example purpses that 'myHeader' references a collapsible header...
if( window.getComputedStyle( myHeader, null ).getPropertyValue( &quot;cursor&quot; ) === &quot;default&quot; ){
// remove the role, aria, and tabindex attributes from the heading and div
removeAttributes();
}
else {
// add the role, aria, and tabindex attributes from the heading and div
addAttributes();
}
});
</code></pre>
<p>This approach worked well for our toggle components, and since we happen to use this “collapsible” control for a large number of features throughout the site, the change had a large impact on improving the accessibility of the site as a whole.</p>
<h4>Toggling dialog role &amp; state</h4>
<p>Similar to the content toggles, we also sometimes use modal dialog controls for informational (or functional) content may be displayed inline in the page layout at certain viewport sizes. In other words, in a small viewport, users might see a link that opens some content in a modal dialog, while at wider breakpoints they might see that dialog’s content displayed in the side column of the page. A couple of cases where this modal/inline pattern has made sense for us are a user sign-in/signup form, and the list of faceted filters that pair with a search result page (the final example on <a href="http://master.origin.dialog.fgview.com/demos/">this dialog demo page</a> shows a functional example of the behavior).</p>
<p>The HTML for an accessible dialog component requires its own set of attributes, such as a <code>role=&quot;dialog&quot;</code> attribute on its container element, but we were able to use the same CSS listening pattern from the collapsible toggle to transition between the interactive or static role of the dialog. For the dialog, we deemed it the control “non-interactive” if the dialog element satisfied 2 conditions: 1) the dialog element was visible to all users (i.e. <em>not</em> <code>display: none</code> or <code>visibility: hidden</code>) and, 2) the dialog offered no visible means of user dismissal (i.e. if its “close” button was not visible to all users).</p>
<p>Notably, at breakpoints where the dialog content was visible in the page, the link that formerly opened the dialog became a simple anchor link that would, upon clicking it, scroll to (and jump focus to) the element that was formerly a dialog. Neat!</p>
<h4>Custom CSS properties instead?</h4>
<p>A more clever approach to this same problem was sent to us by Michael Scharnagl. Michael’s article, <a href="https://justmarkup.com/log/2017/01/handling-states-on-resize-using-css-custom-properties/">Handling states on resize using CSS custom properties</a>, demonstrates how to use custom CSS properties to pass information to JavaScript, such as the expanded or collapsed state of a component. What’s nice about Michael’s approach is that it removes the need for the JavaScript to know any particulars about the styles involved in the component’s presentation (which as he notes, could also be said of an older, slightly hackier technique (<em>his words not mine!</em>) that <a href="https://www.lullabot.com/articles/importing-css-breakpoints-into-javascript">uses CSS pseudo-content to pass information to JavaScript</a>).</p>
<p>As support for <a href="http://caniuse.com/#feat=css-variables">CSS custom properties</a> continues to improve, we will consider using Michael’s approach ourselves.</p>
<h3 id="focus">Focus order</h3>
<p>Last on the problem checklist above is the order in which focus will rove around a layout at different breakpoints. The problem in this case that while CSS allows us to responsively reflow between vastly different positioned/floated/flexboxed layouts across viewport sizes, the keyboard focus order (and indeed, the content reading order for screen readers) follows the order of the elements in the page source. Some screen reader software, like VoiceOver, highlights the text it is currently reading, which does help, but only as much as a visible focus state otherwise would. As you might imagine, when this communicated order jumps around in an order that doesn’t closely match a left-right-top-bottom visual interface, it can be quite disorienting, particularly for those who use screen readers as an aid to the visual layout.</p>
<p>Many accessibility advocates, <a href="https://www.w3.org/TR/WCAG20-TECHS/C27.html">and also the w3c</a>, recommend that designers not stray from source order when creating a CSS layout, which would indeed avoid this issue entirely. Of course, the majority of layouts we see on the web do not strictly follow HTML source order, and for good reason: across viewport sizes, the usability of a design can be dramatically improved by shifting the visual hierarchy, scale, and order of the elements in a page. Indeed, the great promise of CSS was to free us from the constraints of binding our HTML to any particular visual presentation.</p>
<h4>No great solutions…</h4>
<p>It seems this problem is only going to get worse now that advanced CSS layout is widely supported, and it certainly doesn’t help that we now responsively toggle between many of these advanced layouts depending on viewport size. Unfortunately, the tools we have as web developers seem to be inadequate for addressing this issue properly. Given the choices of A) dynamically adapting our HTML source order for every breakpoint, B) sending different HTML sources to each client, or C) renumbering the tabindex attributes of all focusable elements to match their rendered order, we emphatically choose option D) “Nope”.</p>
<p>For our client’s particular tab order issues, we weren’t able to do much to improve the tab order in some of our layouts, but we did add some navigational cues within the markup to help mitigate the problem. For example, when we had a two column layout which at some breakpoints, had a left column that came <em>after</em> the right column in source order, we sometimes used an anchor at the top of the first content in the source that linked to the subsequent content. More importantly though, the use of ARIA landmark roles seemed to aid in orientation here, as screen readers provide their own methods for navigating between landmarks.</p>
<h4>Whose job is this?</h4>
<p>Seemingly, this issue should be addressed at the browser implementation level rather than leaving it to developers to work out. Wouldn’t it be nice if the browser just figured out an appropriate tab order for a given layout? Relatedly, during the time we were testing our client’s site, we were encouraged to find that Firefox had implemented flexbox so that tab focus order would indeed follow the order that the elements were rendered visually in the page. This seemed like the right solution to a problem that’s just too big for web authors to address. Unfortunately, Mozilla since <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=812687">deemed that behavior to be a bug and “fixed” it</a>, so it now works like all other browsers we know of.</p>
<p>In <a href="http://tink.uk/flexbox-the-keyboard-navigation-disconnect/">Flexbox &amp; the keyboard navigation disconnect</a>, Léonie Watson writes:</p>
<blockquote>
<p>CSS Flexbox can create a disconnect between the DOM order and visual presentation of content, causing keyboard navigation to break. For this reason, the CSS Flexible Box Layout module warns against resequencing content logic, but asking authors not to use flexbox in this way seems illogical in itself.
TLDR: The only viable way (in my opinion) for the flexbox disconnect to be resolved, is in the browser (as with the Firefox “bug”) and the accessibility tree.</p>
</blockquote>
<p>While admitting our limited understanding of all the concerns, we’d tend to agree. It would be great to see focus order match the visually rendered order, and not just for flexbox, but with all CSS layout (float, position, etc.). Examples abound. Filament Group’s site is fairly linear in most of our pages, but <a href="/">our homepage’s</a> presentation flips some of its content vertically (not just horizontally) at different viewport sizes:</p>
<p>Homepage, small viewport:
<img src="/images/a11ypost-taborder-sml.png" alt="&quot;Collapsed Navigation toggle&quot;"></p>
<p>Homepage, large viewport:
<img src="/images/a11ypost-taborder.png" alt="&quot;Expanded Navigation toggle&quot;"></p>
<p>We think the wider screen layout of our homepage is better with that shift, but the tab order is indeed a little wacky as a result.</p>
<h2 id="how-about-you%3F">How about you? <a class="direct-link" href="#how-about-you%3F" aria-hidden="true">#</a></h2>
<p>Thanks for reading! I’ve covered some of the issues we’ve recently run into at the intesection of responsive design and accessibility, but we’re sure there are many more to consider. Have you encountered any issues similar to these? I’m researching a talk about this and would love to hear what you’ve found. Please <a href="https://twitter.com/filamentgroup">write to us on Twitter</a> where we can discuss it more.</p>
<p><em><strong>A special thanks</strong> to folks who were kind enough to give early feedback on this post and the concepts that led up to it: Reinhard Stebner, Heydon Pickering, Marcy Sutton, Dave Rupert, James Craig, Ethan Marcotte.</em></p>
Modernizing our Progressive Enhancement Delivery2017-01-12T19:00:00-05:00<h1>Modernizing our Progressive Enhancement Delivery</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> on 01/13/2017</p>
<p>For more than a decade here at Filament Group, we’ve been scrutinizing and updating our workflow for delivering broadly accessible, fault-tolerant websites. Much of that time was spent making small, subtle refinements, but there were several moments where we made larger and philosophical changes to the way we deliver sites as well.</p>
<p>In my mind, the first of those came when we <a href="http://alistapart.com/article/testdriven">began using client-side feature tests</a> to make better decisions about whether—<em>or not!</em>—to apply enhancements on top of an already-usable HTML page. A second shift came from embracing <a href="http://alistapart.com/article/responsive-web-design">responsive design</a>, as that enabled us to serve a catered, appropriate visual layout to a rapidly-widening spectrum of screen sizes without resorting to any forks in our delivery. More recently, a third leap came from focusing on <a href="https://www.filamentgroup.com/lab/delivering-responsibly.html">streamlining the “critical path” to a usable page</a> by removing everything that blocks it from rendering immediately upon arrival.</p>
<p>This week, we’ve put the final touches on some new techniques that helped us further speed up our site load time and dramatically reduce our reliance on network requests. We feel like it will be a game changer for our own site, and will inform our recommendations to our clients moving forward.</p>
<h2 id="a-new-milestone">A New Milestone <a class="direct-link" href="#a-new-milestone" aria-hidden="true">#</a></h2>
<p>This site may not look any different than it did last week (it’s largely identical, really), but we’ve made big changes to the way it’s delivered thanks to two web standards that have recently stabilized in browsers and on servers as well, and they both aim to reduce our reliance and time spent on network requests.</p>
<h3 id="http%2F2-for-speed">HTTP/2 for speed <a class="direct-link" href="#http%2F2-for-speed" aria-hidden="true">#</a></h3>
<p>First, we’ve updated our server environment to utilize the newest iteration of the web’s beloved HTTP protocol: <a href="https://http2.github.io/faq/">version 2!</a> “H2,” for short, brings some incredible features such as “connection coalescing” which allows multitudes of file requests to simultaneously share the same TCP connection, and “server-push” which allows the server to smartly respond to requests with not just the requested file, but any other dependencies it deems relevant as well, which means less time waiting for high-latency trips between the client and the server. <a href="http://caniuse.com/#feat=http2">Browser support for H2 is already very good</a>, and the feature works as an opt-in so that non-H2 browsers can still use our site as they always have in good ol’ HTTP/1.1.</p>
<h3 id="service-worker-for-offline-%26-unstable-connections">Service Worker for offline &amp; unstable connections <a class="direct-link" href="#service-worker-for-offline-%26-unstable-connections" aria-hidden="true">#</a></h3>
<p>Second, our site is now offline-friendly! Now, most modern browsers will look for locally-stored versions of our files <em>before</em> reaching out to our web server (this approach is often referred to as “offline-first”). The result is that much of our site will be accessible when many of our users are offline or browsing over unstable networks.</p>
<p>The feature that makes this possible is called <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API">Service Worker</a>. It’s a new API we can use that enables parts of our JavaScript to act as a proxy between the browser and the server, intercepting and managing requests and responses, and storing or retreiving files from cache. Service Worker is a better—and admittedly much more complicated—version of Appcache, the notoriously-flawed feature that first enabled offline behavior.</p>
<hr style="margin: 1.5em 0">
<p>That’s the high-level view of what’s new. Below, we’ll cover some of the technical nitty-gritty of how we integrated these new technologies. If that part is not your bag, thanks so much for reading! Please do <a href="mailto:hello@filamentgroup.com">give us a holler</a> if you need help designing a speedy and resilient site of your own.</p>
<p>Still here? Alright, let’s dig into how we’ve integrated these features into (or largely, <em>on top of</em>) our progressive enhancement workflow, and the measurable impact they’ve made.</p>
<h2 id="a-technical-overview-of-what%E2%80%99s-new">A Technical Overview of What’s New <a class="direct-link" href="#a-technical-overview-of-what%E2%80%99s-new" aria-hidden="true">#</a></h2>
<p>Delivery starts on the server, and server setup is pretty simple. I’ll cover that first.</p>
<h3 id="server-setup-with-http%2F2">Server Setup with HTTP/2 <a class="direct-link" href="#server-setup-with-http%2F2" aria-hidden="true">#</a></h3>
<p>The files and content of our site are managed by the lovely <a href="https://jekyllrb.com/">Jekyll static site generator</a>. When we deploy our site, the files from Jekyll are copied over to a Linux server and served securely over https (note: <a href="https://letsencrypt.org/">TLS</a> is required both for H2 and for Service Worker) by <a href="https://httpd.apache.org/">Apache</a>. The Apache server has had some nice updates lately, and it now includes great support for HTTP/2.</p>
<p>Contrary to how difficult <a href="/about/#john-bender">John</a> wanted us to believe it was for him, enabling “H2” was merely a matter of enabling a module in our virtual host configuration (sorry, John):</p>
<pre><code>Protocols h2 http/1.1
</code></pre>
<p>…that’s it! With that change, our site immediately started upgrading most of our users to the new protocol, which made our pages <em>finish</em> loading sooner than they used to, since all requests could now be made in parallel rather than in limited batches at a time. Admittedly, our site does not reference a great deal of assets, so the results from this step are not as dramatic as they’d be on a more media-heavy site.</p>
<p>H2 can be enabled in a variety of server environments now, including NGINX, Node, and probably whatever you are using. There’s a bit more we’re doing with H2 on the new site, but I’ll come back to that after some background on how we’re delivering our HTML files.</p>
<h3 id="html-delivery">HTML Delivery <a class="direct-link" href="#html-delivery" aria-hidden="true">#</a></h3>
<p>Our highest priority in the delivery process is to serve HTML that’s ready to begin rendering immediately upon arrival in the browser. Notably, that’s not the way sites typically work. Usually, the browser requests an HTML document, and after downloading that document, it begins parsing the HTML and finds references to CSS and JavaScript files that it must retrieve from the server before it can render the page for the user. The steps involved in this process form a period often referred to as the Critical Rendering Path, and that path lengthens with every additional asset the browser must fetch before rendering the page, or more concretely, each trip to the server and back increases the time a user must wait before viewing the HTML content they already downloaded.</p>
<p>That’s not good, but it’s not an easy problem to fix either. In order to begin addressing this problem, we try to enforce a couple of rules in our delivery:</p>
<ol>
<li>Any assets that are critical to rendering the first screenful of our page should be included in the HTML response from the server.</li>
<li>Any assets that are not critical to that first rendering should be delivered in a non-blocking, asynchronous manner.</li>
</ol>
<p>Now, in the past (in HTTP/1x that is), our only option for addressing #1 above was to <em>inline</em> all assets in question, meaning any critical CSS would be stuffed into a <code>&lt;style&gt;</code> element in the <code>&lt;head&gt;</code> of the HTML document, and any critical JavaScript (e.g. scripts used for running feature tests and bootstrapping page enhancements) could be stuffed into a <code>&lt;script&gt;</code> element in the <code>&lt;head&gt;</code> of the page. Of course, a typically large portion of the CSS rules in a site’s stylesheet are not critical to rendering one given page on the site, meaning inlining all of those rules in the top of an HTML page would be wasteful. In addition to the waste, inlining that much code would also be detremental to our goals, since the initial download of HTML from the server often only includes about 14kb of compressed text, and CSS files are often larger than that on their own. When inlining, we want to fit as much as we can in that first 14kb, so to reduce the weight of the “critical CSS,” we use <a href="https://github.com/filamentgroup/criticalCSS">tools that extract just the portion of the CSS relevant to rendering the top portion of each template on our site</a>. An automated critical CSS tool will run through each unique template on our site and generate a “critical” CSS file, which it saves to a file that we can include in each HTML file as part of our build.</p>
<p>As our workflow goes, we deliver this combined version of an HTML page to all first time visitors to our site, while asynchronously loading (read: loading without blocking page rendering) all of the other assets that the page references, including the site’s full CSS and JavaScript, which are then cached for later. Subsequent visits to any page on the site receive HTML that references the full assets normally, under the assumption that they’ll be cached and no longer necessary to include inline.</p>
<h3 id="enter%2C-http%2F2-server-push">Enter, HTTP/2 Server Push <a class="direct-link" href="#enter%2C-http%2F2-server-push" aria-hidden="true">#</a></h3>
<p>Inlining is a measurably-worthwhile workaround, but it’s still a workaround. Fortunately, HTTP/2’s Server Push feature brings the performance benefits of inlining without sacrificing cacheability for each file. With Server Push, we can respond to requests for a particular file by immediately sending additional files we know that file depends upon. In other words, the server can respond to a request for <code>index.html</code> with <code>index.html</code>, <code>css/site.css</code>, and <code>js/site.js</code>!</p>
<p>In Apache, H2 Push is on by default, and you can specify resources that should be pushed by either adding a <code>link</code> header to the response, or using the slightly better <code>h2PushResource</code> directive. Here’s how that <code>index.html</code> example would look inside an Apache virtualhost or htaccess file:</p>
<pre><code>&lt;If &quot;%{DOCUMENT_URI} == '/index.html'&quot;&gt;
H2PushResource add css/site.css
H2PushResource add js/site.js
&lt;/If&gt;
</code></pre>
<p>In English that says, “if the path to the responding document is <code>/index.html</code>, push these additional css and javascript files.” On our website, we’re using Server push like this to immediately send all of the assets that a page needs to render, including the small critical files we would have inlined in the HTML, had we been using HTTP/1.1. Let’s look at that in code.</p>
<h4>Our H2 Push Configuration</h4>
<p>Every page on our site has a short list of assets (scripts, stylesheets, fonts, images) that they all reference, so we push those on the first visit to <em>any</em> HTML file:</p>
<pre><code>&lt;If &quot;%{DOCUMENT_URI} =~ /\.html$/&quot;&gt;
H2PushResource add /js/dist/head.js?01042017 critical
H2PushResource add /js/dist/foot.js?01042017 critical
H2PushResource add /css/dist/bg/icons.data.svg.css
H2PushResource add /css/type/sourcesanspro-semibold-webfont.woff2
H2PushResource add /css/type/sourcesanspro-light-webfont.woff2
H2PushResource add /css/type/sourcesanspro-lightit-webfont.woff2
H2PushResource add /css/dist/all.css?01042017
H2PushResource add /js/dist/all.js?01042017
H2PushResource add /images/dwpe-bookcover-lrg.png
H2PushResource add /images/rrd-book.png
&lt;/If&gt;
</code></pre>
<p>Each unique template on our site also has a unique critical CSS file that should be pushed with it, so we have conditional push directives for the pages that use each of our templates as well:</p>
<pre><code>&lt;If &quot;%{DOCUMENT_URI} == '/portfolio/index.html'&quot;&gt;
H2PushResource add /css/dist/critical-portfolio.css?01042017
&lt;/If&gt;
&lt;If &quot;%{DOCUMENT_URI} == '/code/index.html'&quot;&gt;
H2PushResource add /css/dist/critical-code.css?01042017
&lt;/If&gt;
</code></pre>
<p>And lastly, we use a cookie to make sure we only push assets the first time a given user visits our site. Ideally with H2, the browser should be able to issue a cancel to the server when it tries to push assets that the browser already has in cache, but in practice this is unfortunately not always true, and we saw the server occasionally re-push cached assets before the browser could say “no thanks.”</p>
<p>In the future, <a href="https://calendar.perfplanet.com/2016/cache-digests-http2-server-push/">Cache Digest lists may be sent with request headers</a> so that the server can be informed of the files a browser has in cache. For now though, we can mimic this by setting a cookie on first visit; if that cookie is already set, we don’t push.</p>
<h4>Readying our HTML Templates for Push (or not!)</h4>
<p>Within our HTML templates on the server side, we’re able to use environment variables to detect the version of HTTP that is in play on a given request. With that knowledge, we can configure our HTML to either inline our critical files (if it’s HTTP/1.1), or reference them externally knowing they’ll be pushed along with the HTML (if it’s HTTP/2).</p>
<p>You can check the protocol most any server-side language such as PHP, or whatever you happen to use. In the old trusty SSI syntax we happen to use on this site (remember SSI!?), our H2 detection checks look like this:</p>
<pre><code>
&lt;!--#if expr=&quot;$SERVER_PROTOCOL=/HTTP\/2\.0/&quot; --&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;/css/dist/critical-portfolio.css?01042017&quot;&gt;
&lt;!--#else --&gt;
&lt;style&gt;
&lt;!--#include virtual=&quot;/css/dist/critical-portfolio.css?01042017&quot; --&gt;
&lt;/style&gt;
&lt;!--#endif --&gt;
</code></pre>
<p>We apply this same logic for the few critical files that we’d normally include inline, like <a href="https://www.filamentgroup.com/js/head.js">the small bootstrapping JavaScript we use in the <code>&lt;head&gt;</code> of our page</a>, and <a href="https://www.filamentgroup.com/js/dist/foot.js">the JavaScript</a> we use to <a href="https://www.filamentgroup.com/lab/font-events.html">progressively apply webfonts once they finish loading</a>.</p>
<h3 id="seeing-h2-in-action">Seeing H2 in action <a class="direct-link" href="#seeing-h2-in-action" aria-hidden="true">#</a></h3>
<p>By running our page through <a href="http://WebPageTest.org">WebPageTest.org</a>, we can see our H2 optimizations working. Check out this pile of parallel requests in the first 600 milliseconds of our page loading (which happens to be the time it takes to get a usable page on our site, on a typical wifi connection).</p>
<p><img src="/images/h2/h2waterfall.png" alt=""></p>
<p>Notably, we’re not currently using a CDN on this site (though we do recommend using one), so our request times vary depending on physical location in the world. We also spend more time in our DNS and SSL handshake than we would like, so that could use some tuning as well. But for now, this feels plenty fast and resilient.</p>
<p>There are more steps to our page enhancement process, but that covers the parts that were impacted by updating to H2. As I mentioned early on, you can read about our holistic page delivery workflow (before these new H2 improvements) in <a href="https://www.filamentgroup.com/lab/delivering-responsibly.html">Delivering Responsibly</a></p>
<h2 id="our-service-worker-implementation">Our Service Worker Implementation <a class="direct-link" href="#our-service-worker-implementation" aria-hidden="true">#</a></h2>
<p>In addition to the H2 update, we’ve integrated a Service Worker as well. The Service Worker API is the new suite of features that enable our sites to work offline, make smarter caching decisions, and be more tolerant of inconsistent network connectivity. If Service Worker is new to you (it was for us!), there are many great primers out there. Personally, I found Lyza Danger Gardner’s post <a href="https://www.smashingmagazine.com/2016/02/making-a-service-worker/">Making a Service Worker</a> helpful, in addition to some good old view-source reverse engineering of our good friend <a href="https://adactio.com">Adactio</a>’s site (Thanks Jeremy!).</p>
<h3 id="caching-%26-offline-strategy">Caching &amp; Offline Strategy <a class="direct-link" href="#caching-%26-offline-strategy" aria-hidden="true">#</a></h3>
<p><a href="https://www.filamentgroup.com/sw.js">Our use of service worker</a> is pretty standard. For starters, on first visit we use service worker to cache the shared assets listed in our H2 server push list above so that they’ll be available locally the next time they’re requested. However, in this step we did run into a small hangup. The service worker begins fetching files once the page has finished loading, so by the time the worker fetches its assets, they’ve already been downloaded once and placed in cache. We wanted to ensure that the worker pulls these files from the browser cache instead of requesting them fresh from the web, but this is a touchy process, as even minor differences between the first and subsequent request headers can instruct the service worker to ignore an already cached file and request a fresh copy. In our case, we had mistakenly set our <code>vary</code> header to <code>&quot;Cookie&quot;</code> on all of our file requests, which tells the browser that these assets should be expected to <em>vary</em> their response content based on the presence of cookies. In practice, we only wanted <code>vary</code> headers on our HTML files, since those are the only assets that do indeed vary based on cookies. But since we had this header set on all of our files, all of the requests that the service worker made ended up carrying cookies that were not present the first time the same requests were made, and it wouldn’t fetch from browser cache. This was resolved by setting vary only on our HTML files, but I wanted to note it since it hung us up.</p>
<p>In addition to the static assets, we also fetch the HTML for the top-level pages of our site so that the primary navigation works if you were to lose connectivity after loading the first page, and a fallback offline page that will be served if the user is offline and tries to access a page that is not in cache. When we fetch these pages, we make sure to include request credentials so that the cookies are carried and the worker reaches out to the server to get versions of our HTML pages that do not include the Critical CSS.</p>
<p>Here’s a view from Chrome’s Network tab showing the initial requests made by our service worker. Note that the top-level pages are requested from the server, while the assets are fetched from browser cache:</p>
<p><img src="/images/h2/sw-requests.png" alt=""></p>
<p>Beyond the shared assets and top-level pages, the service worker will cache all of the pages and files you request as you browse around the pages of our site. If you happen to drop offline, if a page is already in cache, it will load just fine. If it’s not in cache, you get the <a href="https://www.filamentgroup.com/offline.html">fallback page</a> that suggests you’ve seemingly lost connectivity. Lastly, we borrowed a neat little trick from Adactio in which the service worker cleverly returns an SVG image that says “offline” for any image requests that fail when the user is offline.</p>
<h2 id="progressive-web-appy-goodness">Progressive Web Appy Goodness <a class="direct-link" href="#progressive-web-appy-goodness" aria-hidden="true">#</a></h2>
<p>In addition to H2 and service worker, our site references <a href="https://www.filamentgroup.com/webappmanifest.json">a web app manifest file</a>, which provides a device some meta information (site name, theme colors, app icons, etc.) about our site in the event that a user decides to “install” it as an app. With all these pieces in place, our site can be deemed a bonafied Progressive Web App. Just looks at this perfect score on <a href="https://chrome.google.com/webstore/detail/lighthouse/blipmdconlkpinefehnmjammfjpmpbjk">Google’s Lighthouse validator</a> tool:</p>
<p><img src="/images/h2/lighthouse.png" alt=""></p>
<p>As a progressive web app, this site will prompt users in supporting browsers to ask if they would like to install our site as an app. So far, “installing” our site just means that it gets a little app icon on the device’s homescreen, which isn’t much different than bookmarking any site to your homescreen. But there are other features some devices support that we can use as well, such as sending notifications to the user (perhaps when we publish a blog post?). We expect that additional app-like features will become available to progressive web apps over time as well.</p>
<h2 id="thanks!">Thanks! <a class="direct-link" href="#thanks!" aria-hidden="true">#</a></h2>
<p>Thanks for hanging in here for the long haul. We’re excited about the benefits that these new technologies provide, and can’t wait to apply what we’ve learned on our own site to better serve our clients. For helping with questions, code examples I’ve cribbed, and reviews of this post, I’d like to give a big thanks to Jeremy Keith, Ethan Marcotte, Jake Archibald, Lyza Gardner, Pat Meenan, Andy Davies, and Yoav Weiss.</p>
<p>As always, if you want to chat about this post, you can find us at <a href="https://twitter.com/filamentgroup">@filamentgroup</a> on Twitter. (And of course, if you’d like us to help you work through a challenge like this for your company’s site or app, <a href="mailto:hello@filamentgroup.com">get in touch</a>!)</p>
I Wanted To Type a Number2016-07-13T00:00:00-04:00<h1>I Wanted to Type a Number</h1>
<p>Posted by <a href="/about/#zach-leatherman">Zach</a> on 07/13/2016</p>
<p>Forms are the bedrock upon which users interact with the web. For a long time, forms controls were stagnant and reliable. HTML5 brought us updates, specifying new <code>&lt;input&gt;</code> types linked with new interface controls, progressively enhancing from the vanilla text inputs of yesteryear. The battle tested truth of new web features and standards proved very true with these new form controls: browser implementations vary.</p>
<p>This browser variability is particularly true with touch devices, especially in relation to the primary innovation of the touchscreen keyboard: its malleability, or adaptation its mode of input to best suit the context. For example, when a site specifies that the user should type a number, the browser can show a number-pad like keyboard (0 through 9) with extra large buttons for easier, faster, and more accurate numeric input. The usability difference between the small button and large button keyboards for numeric input is stark:</p>
<figure>
<img src="/images/number-blog-post/android-6-type-text.png" alt="">
<figcaption>`type="text"` Keyboard on Android 6 (Samsung Galaxy S7)</figcaption>
</figure>
<figure>
<img src="/images/number-blog-post/android-6-type-number.png" alt="">
<figcaption>`type="number"` Numeric Keyboard on Android 6 (Samsung Galaxy S7)</figcaption>
</figure>
<p>But how does the browser know when to show this improved numeric keyboard? How can a developer inform the browser of the user’s intent and context? Of course, variability exists there too.</p>
<p>Consider a few examples of the many different types of numeric input we may need to capture on our web forms:</p>
<ol>
<li>Prices (sometimes integers with thousands separators, but often used as non-integers)</li>
<li>Zip Codes (in the US these can contain leading zeros)</li>
<li>Credit Cards (very long numbers)</li>
<li>Credit Card Security Codes: CVV, CVC, or CSC (may also have leading zeros)</li>
<li>Gift cards (formats vary)</li>
<li>Ages</li>
<li>Years</li>
<li>Times (Minutes or Seconds may have leading zeros)</li>
<li>Phone Numbers</li>
</ol>
<p>You might assume to just use <code>&lt;input type=&quot;number&quot;&gt;</code> for all of the above. Unfortunately, that assumption would be wrong.</p>
<h2 id="let%E2%80%99s-consult-the-specification">Let’s consult the Specification <a class="direct-link" href="#let%E2%80%99s-consult-the-specification" aria-hidden="true">#</a></h2>
<p>Wait, come back! The <a href="https://www.w3.org/TR/html-markup/input.number.html">HTML5 specification for <code>input type=&quot;number&quot;</code></a> will provide us with clues to what kind of input will be accepted by the control, so let’s level-set this thing.</p>
<p>The most important piece of this puzzle is <a href="https://www.w3.org/TR/html-markup/input.number.html#input.number.attrs.value">the value</a> which must be a <a href="https://www.w3.org/TR/html-markup/datatypes.html#common.data.float">Floating Point Number</a>.</p>
<p>Floating Point Numbers are comprised of one or more characters in the range 0—9 (numbers, yeah?), optionally followed by a decimal point and more 0—9 characters. Floating Point Numbers may also have a leading <code>-</code> to denote a negative number.</p>
<p>For simplification purposes note that I’ve omitted the scientific notation part of the specification, which we will try to avoid showing users as well (we’ll explain more about this later).</p>
<p>The important and problematic thing here is that values that technically obey the floating point specification definition above may work with <code>&lt;input type=&quot;number&quot;&gt;</code>. Note the [last green note on the W3 HTML5 forms specification](<a href="https://www.w3.org/TR/html5/forms.html#number-state-(type=number)">https://www.w3.org/TR/html5/forms.html#number-state-(type=number)</a>):</p>
<blockquote>
<p>The type=number state is not appropriate for input that happens to only consist of numbers but isn’t strictly speaking a number. For example, it would be inappropriate for credit card numbers or US postal codes. A simple way of determining whether to use type=number is to consider whether it would make sense for the input control to have a spinbox interface (e.g. with “up” and “down” arrows).</p>
</blockquote>
<p>This leaves us with some ambiguity. Minutes and Seconds fields are obvious counter-examples to the specification’s language. Spinbox would be valuable there, but those fields may need leading zeros.</p>
<p>From the notes in the specification, we can glean that <code>type=&quot;number&quot;</code> is intended for numbers like Prices, Ages, and Years; <code>type=&quot;tel&quot;</code> for Phone Numbers; <code>type=&quot;text&quot;</code> for everything else. <em>This leaves a lot of numeric input fields with the undesirable small button, default <code>type=&quot;text&quot;</code> keyboard.</em></p>
<figure>
<img src="/images/number-blog-post/android-6-type-text.png" alt="">
<figcaption>`type="text"` Keyboard on Android 6 (Samsung Galaxy S7)</figcaption>
</figure>
<h2 id="useful-pragmatism">Useful Pragmatism <a class="direct-link" href="#useful-pragmatism" aria-hidden="true">#</a></h2>
<p>There very useful ways to opt-in to the big button numeric keyboard, available for use in browsers today:</p>
<ul>
<li><code>type=&quot;number&quot;</code></li>
<li>The <code>pattern</code> attribute (iOS only)</li>
<li><code>type=&quot;tel&quot;</code></li>
</ul>
<h3 id="type%3D%22number%22"><code>type=&quot;number&quot;</code> <a class="direct-link" href="#type%3D%22number%22" aria-hidden="true">#</a></h3>
<p>We can choose between following the specification and having a sub-optimal keyboard for numeric input on a large swath of devices, or we can explore other options to make this work. In testing on Android (both Chrome and the older Android Browser), Firefox for Android, and Windows Phone, the only way to trigger a big button keyboard is to ignore the W3C specification and use <code>type=&quot;number&quot;</code>.</p>
<form class="inlinedemo">
<h4 class="inlinedemo_title">Try it in your browser</h4>
<label class="clearfix">
<code>type="number"</code>
<input type="number">
</label>
</form>
<p>On the same Android operating system version Android big button numeric keyboard implementations vary—and many do not correctly implement the specification’s requirements for negative numbers and decimal points.</p>
<figure>
<img src="/images/number-blog-post/android-23-samsung-galaxy-s2.png" alt="">
<figcaption><strong>No decimal point</strong>: Android 2.3 (Samsung Galaxy S2) with <code>type="number"</code></figcaption>
</figure>
<figure>
<img src="/images/number-blog-post/android-23-motorola-droid-razr.png" alt="">
<figcaption><strong>Looks like a telephone input</strong>: Android 2.3 (Motorola Droid Razr) with <code>type="number"</code></figcaption>
</figure>
<figure>
<img src="/images/number-blog-post/android-6-type-number.png" alt="">
<figcaption><strong>No negative sign</strong>: Android 6 (Samsung Galaxy S7) with <code>type="number"</code></figcaption>
</figure>
<p>Perhaps unsurprisingly to veteran web developers, Firefox is closest to the specification here, with reliable, dedicated buttons for both decimal points and negative signs.</p>
<figure>
<img src="/images/number-blog-post/android-6-firefox-47-type-number.png" alt="">
<figcaption><strong>Looks great</strong>: Firefox 47 on Android 5.1.1 (Motorola Nexus 6) with <code>type="number"</code></figcaption>
</figure>
<p>Windows Phone has no support for negative numbers on their <code>type=&quot;number&quot;</code> keyboard either.</p>
<figure>
<img src="/images/number-blog-post/windows-phone-type-number.png" alt="">
<figcaption><strong>No negative sign</strong>: Windows Phone 8.1 (Lumia 930) with <code>type="number"</code></figcaption>
</figure>
<p>On iOS, the <code>type=&quot;number&quot;</code> keyboard is different than <code>type=&quot;text&quot;</code> (and is safe to use per the specification: it has both keys for negative and decimal points) but is not the big button user experience we’re going for.</p>
<figure>
<img src="/images/number-blog-post/ios-9-type-number.png" alt="">
<figcaption><strong>Small buttons</strong>: iOS 9 with <code>type="number"</code></figcaption>
</figure>
<h3 id="the-pattern-attribute">The <code>pattern</code> Attribute <a class="direct-link" href="#the-pattern-attribute" aria-hidden="true">#</a></h3>
<p>The big button numeric keyboard on iOS (on an iPhone) is shown when the <code>pattern</code> attribute has the value <code>[0-9]*</code> or <code>\d*</code>. iOS (on an iPhone) doesn’t care what the <code>type</code> attribute value is. It could be <code>text</code>, <code>number</code>, et cetera. But if <code>pattern=&quot;[0-9]*&quot;</code> or <code>pattern=&quot;\d*&quot;</code>, iOS (on an iPhone) will show a big button numeric keypad.</p>
<p>The big button numeric keyboard does not exist on the iPad variant of iOS—only on the iPhone version. With more screen real estate to work with on an iPad, using an approved <code>pattern</code> value, <code>type=&quot;tel&quot;</code>, or <code>type=&quot;number&quot;</code> all use the small button numeric keyboard shown above.</p>
<figure>
<img src="/images/number-blog-post/ios-9-type-text-pattern.png" alt="">
<figcaption><strong>Looks like a telephone input</strong>: iOS 9 (on an iPhone) with <code>type="text" pattern="[0-9]*</code></figcaption>
</figure>
<p><em>It should be noted that functionally equivalent regular expressions with the same output do not show a numeric keyboard, e.g. <code>[0123456789]*</code> or <code>[0-9]{0,}</code> or <code>[0123456789]{0,}</code>.</em></p>
<p>Logically because iOS (on an iPhone) ties this keyboard to a numbers-only <code>pattern</code> regex, buttons for the negative sign or the decimal point simply don’t exist on the big button keyboard. Importantly, iOS (on an iPhone) does not let the user switch keyboard types when the big button numeric keyboard is displayed. Entering true floating point numbers is simply not allowed, per the <code>pattern</code> explicitly defined.</p>
<form class="inlinedemo">
<h4 class="inlinedemo_title">Try it in your browser</h4>
<label class="clearfix">
<code>type="text" pattern="[0-9]*"</code>
<input type="text" pattern="[0-9]*">
</label>
</form>
<p>Take care to also note that the <code>pattern</code> attribute is overloaded to control <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-pattern">HTML5 form validation</a>. This forces developers to choose between a numeric keyboard or a more accurate validation pattern—e.g. a regular expression for a two digit number <code>pattern=&quot;[0-9]{2}&quot;</code> would not use the numeric keyboard.</p>
<p>Unfortunately, the <code>pattern</code> trick for big button numeric keyboards only works on iOS. Other web browsers will display the default <code>text</code> keyboard when the whitelist iOS <code>pattern</code> attribute values are used, so we need to supplement this method with other techniques. Luckily, combining <code>pattern</code> with <code>type=&quot;number&quot;</code> will trigger a big button numeric keypad on all the platforms we’ve discussed thus far.</p>
<form class="inlinedemo">
<h4 class="inlinedemo_title">Try it in your browser</h4>
<label class="clearfix">
<code>type="number" pattern="[0-9]*"</code>
<input type="number" pattern="[0-9]*">
</label>
</form>
<h3 id="type%3D%22tel%22"><code>type=&quot;tel&quot;</code> <a class="direct-link" href="#type%3D%22tel%22" aria-hidden="true">#</a></h3>
<p>If using <code>type=&quot;number&quot;</code> for some of these other free-form number formats goes against the spirit of the specification, using <code>type=&quot;tel&quot;</code> for things that are not phone numbers is a far more egregious semantic crime. I would not consider this a viable future-friendly alternative to <code>type=&quot;number&quot;</code> for numeric input.</p>
<form class="inlinedemo">
<h4 class="inlinedemo_title">Try it in your browser</h4>
<label class="clearfix">
<code>type="tel"</code>
<input type="tel">
</label>
</form>
<h2 id="there-has-to-be-a-better-way">There has to be a better way <a class="direct-link" href="#there-has-to-be-a-better-way" aria-hidden="true">#</a></h2>
<p>Luckily, the web standards people have recognized this mess and have standardized an appetizing alternative: <a href="https://html.spec.whatwg.org/multipage/forms.html#input-modalities:-the-inputmode-attribute">the <code>inputmode</code> attribute</a>. <code>inputmode</code> lets you directly specify which type of keyboard to use, independent of the <code>type</code> attribute value. For example, on a credit card number field we could use <code>type=&quot;text&quot;</code> and <code>inputmode=&quot;numeric&quot;</code> together—problem solved.</p>
<p>At time of writing, <code>inputmode</code> is <a href="http://caniuse.com/#feat=input-inputmode">not supported anywhere</a>. This is really unfortunate—it would alleviate a lot of the mismatched usage of <code>&lt;input type=&quot;number&quot;&gt;</code> butting heads with the specification’s definition. This would help specifically on Android, Firefox on Android, and Windows Phone, which have all hitched big button numeric keyboards to <code>type=&quot;number&quot;</code>.</p>
<p><em>Side note: while <code>inputmode=&quot;numeric&quot;</code> is appetizing and would help with some of the issues, the specification continues to lack a provision for non-integers. Even after browser support for this feature burgeons we’ll still have to use <code>type=&quot;text&quot;</code> without <code>inputmode</code> for non-integers given the unreliability of decimal point buttons on current numeric keyboards.</em></p>
<form class="inlinedemo">
<h4 class="inlinedemo_title">Try it in your browser</h4>
<label class="clearfix">
<code>type="text" inputmode="numeric"</code>
<input type="text" inputmode="numeric">
</label>
</form>
<h2 id="working-around-our-limitations">Working around our Limitations <a class="direct-link" href="#working-around-our-limitations" aria-hidden="true">#</a></h2>
<p>As we explored this problem, it become clear that if we want to provide an efficient way to enter numbers across the broadest range of devices we need to be pragmatic and combine <code>pattern</code> and <code>type=&quot;number&quot;</code>. Since we’re using <code>type=&quot;number&quot;</code> in more places than the specification may endorse, we must travel with great care and iron out wrinkles along the way.</p>
<p>We’ve wrapped what we learned into a small utility called <a href="https://github.com/filamentgroup/formcore#numeric-input"><code>numeric-input</code></a> which lets us use big button numeric keyboards today, while sidestepping some nasty bugs we ran into with numeric inputs. Here’s what it does:</p>
<h3 id="restrict-to-numbers-only">Restrict to numbers only <a class="direct-link" href="#restrict-to-numbers-only" aria-hidden="true">#</a></h3>
<p>Since this is an input designed to capture only numeric values, the <code>numeric-input</code> plugin filters out any character that isn’t a 0-9 when you type, paste, or the value is auto-filled by the browser.</p>
<p>Note: This filtering means the “-” and “.” characters are stripped out so <em>negative numbers</em> or <em>decimals</em> aren’t supported with this plugin. Despite the fact that these are “numbers,” it’s best not to use numeric keyboards for these due to spotty support for negative number and decimal point buttons on numeric keyboards (as you can in the device screenshots above). Use <code>type=&quot;text&quot;</code> for these fields.</p>
<h3 id="support-leading-zeros">Support leading zeros <a class="direct-link" href="#support-leading-zeros" aria-hidden="true">#</a></h3>
<p>Leading zeros used in US Zip Codes or the minutes/seconds are dropped on blur in <a href="https://github.com/scottjehl/Device-Bugs/issues/71">Safari 6</a>). For example, a zip code would be changed from “01234” to “1234”. Our plugin simply toggles <code>type=&quot;number&quot;</code> fields to <code>type=&quot;text&quot;</code> in older Safari so the value is left alone. Luckily, the <code>pattern</code> trick still ensures the large keypad on older iOS.</p>
<h3 id="prevent-rounding-on-large-numbers">Prevent rounding on large numbers <a class="direct-link" href="#prevent-rounding-on-large-numbers" aria-hidden="true">#</a></h3>
<p>Large numbers longer than 16 digits are rounded in <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1005603">Firefox (desktop, not Android or iOS)</a>. For example, <code>el.value = &quot;9999999999999999&quot;;</code> renders <code>10000000000000000</code>. This is a big problem for entering 16 digit credit cards so the plugin also toggles <code>type=&quot;number&quot;</code> fields to <code>type=&quot;text&quot;</code> in Firefox (Desktop) to work around this bug.</p>
<h3 id="support-maxlength">Support <code>maxlength</code> <a class="direct-link" href="#support-maxlength" aria-hidden="true">#</a></h3>
<p>If we want to enforce a specific value length (say 5 digits for a zip code), there isn’t a way to specify a <code>maxlength=&quot;5&quot;</code> with native numeric inputs. Sure, you can add a <code>max=&quot;99999&quot;</code> attribute but the plugin also adds support for <code>maxlength</code> to enforce how many characters you want to accept. Works with keyboard entry, pasting, and auto-fill.</p>
<h3 id="remove-spinners-%26-arrow-keys">Remove Spinners &amp; Arrow Keys <a class="direct-link" href="#remove-spinners-%26-arrow-keys" aria-hidden="true">#</a></h3>
<p>Desktop browsers have helpful features for incrementing or decrementing the value in a number field: adding small tiny up/down “spinner” arrows inside the fields, binding the keyboard’s arrow up/down keys, and mousewheel events. In some cases this behavior can be very destructive—think how unpredictable accidentally hitting the arrow key in a credit card field would be. <code>numeric-input</code> includes an option (the <code>data-numeric-input-nav-disabled</code> attribute) to disable increment and decrement arrow key behavior and the small spinbox arrows embedded sometimes shown inside the <code>type=&quot;number&quot;</code> inputs <a href="https://github.com/filamentgroup/formcore/blob/dee75699f62cb13cf6fe497e7651b2d03d37d364/css/formcore.css#L204-L211">are easily hidden using CSS</a>.</p>
<figure>
<img src="/images/number-blog-post/chrome-spinner-arrows.png" alt="">
<figcaption>Spinbox arrows on Chrome with <code>type="number"</code></figcaption>
</figure>
<h2 id="using-numeric-input">Using <code>numeric-input</code> <a class="direct-link" href="#using-numeric-input" aria-hidden="true">#</a></h2>
<p>Using the <code>numeric-input</code> plugin is pretty easy:</p>
<pre><code>&lt;input type=&quot;number&quot; pattern=&quot;[0-9]*&quot; data-numeric-input&gt;
</code></pre>
<p>It’s only a small piece of a set of form utilities we’re calling <a href="https://github.com/filamentgroup/formcore"><code>formcore</code></a> which we’ll be adding more plugins to and documentating further in the future.</p>
<form class="inlinedemo">
<h4 class="inlinedemo_title">Try it in your browser</h4>
<label class="clearfix">
<code>data-numeric-input</code>
<iframe src="/examples/numeric-input/numeric-input.html"></iframe>
</label>
</form>
<h2 id="a-few-caveats">A few caveats <a class="direct-link" href="#a-few-caveats" aria-hidden="true">#</a></h2>
<h3 id="scientific-notation-not-supported">Scientific Notation not supported <a class="direct-link" href="#scientific-notation-not-supported" aria-hidden="true">#</a></h3>
<p>Since the plugin strips out any letters, including the “e” character, support <a href="https://en.wikipedia.org/wiki/Scientific_notation#E_notation"><em>scientific and E notation</em></a> isn’t supported. Use <code>type=&quot;text&quot;</code> for these situations.</p>
<h3 id="negative-numbers%2C-non-integers">Negative Numbers, Non-Integers <a class="direct-link" href="#negative-numbers%2C-non-integers" aria-hidden="true">#</a></h3>
<p>If the field requires <em>negative numbers</em> or <em>decimals</em>, there isn’t much <code>numeric-input</code> can do to improve the spotty support for negative number and decimal point buttons on numeric keyboards. Use <code>type=&quot;text&quot;</code> for these situations.</p>
Snapper: A CSS Snap Points Carousel2016-06-15T00:00:00-04:00<h1>Snapper: A CSS Snap Points Carousel</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> on 06/15/2016</p>
<p>Everybody loves carousels, am I right?</p>
<p><em>[…entire room empties out…]</em></p>
<p>But really though, hear me out! We use carousels on many projects and they’re good for a great many things. One of those things is presenting product images neatly within an already-noisy product detail page, say, for <a href="https://www.amazon.com/Armour-Potted-Meat-Spread-3-Ounce/dp/B0025UCGVE/191-2790858-6374923">some Potted Meat on Amazon</a>. Another of those things is, well okay I can’t really think of another example but… again, that first one!</p>
<p>At any rate, if you’re going to use a carousel, it had better work great. For years, we’ve been improving our own <a href="https://github.com/filamentgroup/responsive-carousel">Responsive Carousel</a> to meet the needs of our client projects, and it still serves us well (particularly when we want more of a crossfading slideshow effect). Recently however, we started looking into a new web standard which is specifically designed to handle the sidescrolling carousel pattern. That new standard is CSS Snap Points, and it extends the functionality of CSS “overflow” scrolling regions to be able to “snap” to particular points in their scrolling span.</p>
<h2 id="css-snap-points-in-action">CSS Snap Points in action <a class="direct-link" href="#css-snap-points-in-action" aria-hidden="true">#</a></h2>
<p>To see CSS Scroll Snapping in action, I recommend checking out <a href="https://webkit.org/blog/4017/scroll-snapping-with-css-snap-points/">this Webkit blog post</a> that has a bunch of videos and examples. You’ll need either Firefox, Safari, MS IE or Edge to see the demos working fully (<a href="http://caniuse.com/#feat=css-snappoints">no Chrome support, sadly</a>), but the feature does fall back to ordinary scrolling in unsupporting browsers.</p>
<p>In code, CSS snap points are rather nice, at least compared to what we’d do otherwise. For example, here’s how you’d style a simple scroll-snapping container that has 4 image “slides”, each filling 25% of their parent element’s width, which is 400% of the width of scrollable element. It will “snap” to the start of the slides when the user scrolls it:</p>
<p>HTML:</p>
<pre><code>
&lt;div class=&quot;scrollpane&quot;&gt;
&lt;div class=&quot;scrollpane_items&quot;&gt;
&lt;img class=&quot;scrollpane_item&quot; alt=&quot;...&quot; src=&quot;1.jpg&quot;&gt;
&lt;img class=&quot;scrollpane_item&quot; alt=&quot;...&quot; src=&quot;2.jpg&quot;&gt;
&lt;img class=&quot;scrollpane_item&quot; alt=&quot;...&quot; src=&quot;3.jpg&quot;&gt;
&lt;img class=&quot;scrollpane_item&quot; alt=&quot;...&quot; src=&quot;4.jpg&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>CSS:</p>
<pre><code>.scrollpane {
overflow: auto;
white-space: nowrap;
width: 100%;
-webkit-overflow-scrolling: touch;
/* snap to points */
-webkit-scroll-snap-type: mandatory;
-ms-scroll-snap-type: mandatory;
scroll-snap-type: mandatory;
/* x interval for snapping (100% of container width) */
-webkit-scroll-snap-points-x: repeat(100%);
-ms-scroll-snap-points-x: repeat(100%);
scroll-snap-points-x: repeat(100%);
}
.scrollpane_items {
width: 400%;
overflow: hidden;
}
.scrollpane_item {
width: 25%;
float: left;
}
</code></pre>
<p>JavaScript:</p>
<pre><code>// none! just kidding... keep reading.
</code></pre>
<h2 id="polyfills-and-prollyshouldworks">Polyfills and ProllyShouldWorks <a class="direct-link" href="#polyfills-and-prollyshouldworks" aria-hidden="true">#</a></h2>
<p>For basic use cases, the above example works pretty well. And if you’re okay with a carousel component only snapping in browsers that support CSS snap points (your client probably will not be), then that’s pretty much all you need!</p>
<p>For our purposes, we wanted to use this on a popular ecommerce website, so we needed something a bit more refined. We wanted snapping to work more broadly in browsers that don’t yet support it natively, and we also wanted to integrate features like thumbnail navigation, and next and previous arrows, which tend to help a lot in browsers that don’t show a scrollbar for overflow regions. We also noticed that even in supported browsers, the behavior was not fully what we desired. For example, we wanted our navigation controls (thumbnails and next/prev links) to scroll the carousel smoothly to the target slide, rather than jumping to the slide immediately. We also wanted to maintain a snap location while resizing the browser, and that did not yet work in any implementations we tested.</p>
<h2 id="introducing-snapper">Introducing Snapper <a class="direct-link" href="#introducing-snapper" aria-hidden="true">#</a></h2>
<p>When in trouble, throw some JavaScript at it, right?</p>
<p><a href="http://filamentgroup.github.io/snapper/demo/">Snapper</a> is a lightweight script that’ll apply CSS and JS carefully to create a broadly-functional snap-points carousel. This script builds on a simple overflow container and native CSS snap points to make it better by:</p>
<ul>
<li>feature-testing for CSS Snap Points support and polyfilling the behavior for browsers that don’t support it</li>
<li>Adding a smooth side scroll animation when you navigate (even in browsers that natively support snap points)</li>
<li>Improving navigation by adding optional next/prev buttons, and supporting thumbnail or dot navigation links</li>
<li>Adding the ability to loop back to the start of the carousel when you reach the end</li>
</ul>
<h3 id="demos%2C-code%2C-and-issue-tracker">Demos, Code, and Issue Tracker <a class="direct-link" href="#demos%2C-code%2C-and-issue-tracker" aria-hidden="true">#</a></h3>
<ul>
<li>Demos: You can view several demos of Snapper at the project repo, here: <a href="http://filamentgroup.github.io/snapper/demo/">Snapper Demos</a></li>
<li>Docs: Find docs in the project repo, here: <a href="https://github.com/filamentgroup/snapper">Snapper Demos</a></li>
<li>Code: The code is on <a href="https://github.com/filamentgroup/snapper">Github</a> and <a href="https://www.npmjs.com/package/fg-snapper">NPM</a></li>
<li>Issue tracker: if you run into problems or have ideas, feel free: <a href="https://github.com/filamentgroup/snapper/issues">Snapper Issues</a></li>
</ul>
<p><strong>Note:</strong> Due to the project requirements for which Snapper was developed, it does have a jQuery (or <a href="https://github.com/filamentgroup/shoestring">Shoestring</a>) dependency. We’re very open to refactoring it to remove that dependency as time allows, and would love some help in doing so. So far, the unit tests are largely written to test the outcomes rather than the API, so that may make for an easier transition to “vanilla” JS.</p>
<p>Thanks for reading, and do hit us up on <a href="http://twitter.com/filamentgroup">twitter</a> with any feedback or questions you may have.</p>
Enhancing Optimistically2016-06-10T00:00:00-04:00<h1>Enhancing Optimistically</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> on 06/10/2016</p>
<p>Every so often, we come across ways to improve our more well-trodden core progressive enhancement patterns. Sometimes, we’ll utilize a new web standard to address problems we’d previously approached in a less-optimized manner, while other times we’ll make adjustments to address browser-or-network conditions that could be handled in more fault-tolerant ways. Recently, I came across an example of the latter, and this post will document a small but meaningful way I worked to accommodate it.</p>
<h2 id="serving-condiments">Serving Condiments <a class="direct-link" href="#serving-condiments" aria-hidden="true">#</a></h2>
<p>For <a href="http://alistapart.com/article/testdriven">quite a while now</a>, we’ve been progressively enhancing sites using a pattern that these days many of us refer to as “Cutting the Mustard,” per <a href="https://twitter.com/tmaslen">Tom Maslen’s</a> <a href="http://responsivenews.co.uk/post/18948466399/cutting-the-mustard">great metaphor</a>. As the pattern goes, we run a series of feature tests relevant to enhancements we’d like to make to the page, and if the browser “cuts the mustard” we enable and load additional code that utilizes those features to improve the user experience.</p>
<p>The features we test vary from project to project, but the JavaScript code that performs the tests invariably sits inline in the <code>head</code> of the HTML page, so that it can run immediately when the HTML arrives. It tends to look something like this:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">if</span><span class="token punctuation">(</span> <span class="token string">"querySelector"</span> <span class="token keyword">in</span> window<span class="token punctuation">.</span>document <span class="token operator">&amp;&amp;</span> <span class="token string">"addEventListener"</span> <span class="token keyword">in</span> window <span class="token punctuation">)</span><span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">// This is a capable browser, let's improve the UI further!</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>…in which we’ve tested the browser’s ability to query the DOM using CSS selectors, and its ability to add event listeners.</p>
<p>Tests at this stage are not fine-grained, but they serve as a decent diagnostic of the level of user agent we’re dealing with. Based on these sorts of tests (and sometimes a few more), we can assume that we’re dealing with a browser that is advanced enough to handle additional scripting and UI enhancements, and then <em>apply</em> our enhancements using additional fine-grained feature tests when necessary.</p>
<p>Once a browser passes the test, our first step in enhancing the page further is to add a class to the <code>html</code> element called <code>enhanced</code>, which can then be used to style elements in the page to match the enhanced functionality that they will receive when we apply additional behavior to them—say… converting a list of images into a smooth-scrolling carousel, or a heading/content pair into a toggleable progressive disclosure.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">if</span><span class="token punctuation">(</span> <span class="token string">"querySelector"</span> <span class="token keyword">in</span> window<span class="token punctuation">.</span>document <span class="token operator">&amp;&amp;</span> <span class="token string">"addEventListener"</span> <span class="token keyword">in</span> window <span class="token punctuation">)</span><span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">// This is a capable browser, let's improve the UI further!</span></span><br><span class="highlight-line"> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className <span class="token operator">+=</span> <span class="token string">" enhanced"</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>With this class in play, we can apply the presentation for our enhanced UI using a CSS parent selector, like this:</p>
<pre class="language-css"><code class="language-css"><span class="highlight-line"><span class="token selector">.foo</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">/* basic styles for .foo go here */</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span><br><span class="highlight-line"><span class="token selector">.enhanced .foo</span> <span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">/* enhanced styles for .foo go here */</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>…all of this so far is fairly common and well-known, I should note. But this pattern can be considered optimistic, perhaps too much so. That’s because often, the CSS that we apply using this class hinge the successful loading and application of some JavaScript that we begin to fetch (asynchronously) as soon as we add the class. Like this:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">if</span><span class="token punctuation">(</span> <span class="token string">"querySelector"</span> <span class="token keyword">in</span> window<span class="token punctuation">.</span>document <span class="token operator">&amp;&amp;</span> <span class="token string">"addEventListener"</span> <span class="token keyword">in</span> window <span class="token punctuation">)</span><span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">// This is a capable browser, let's improve the UI further!</span></span><br><span class="highlight-line"> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className <span class="token operator">+=</span> <span class="token string">" enhanced"</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// load the enhanced scripting</span></span><br><span class="highlight-line"> <span class="token function">loadJS</span><span class="token punctuation">(</span> <span class="token string">"/path/to/enhancements.js"</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Above, just after the <code>enhanced</code> class addition, I’ve requested a JavaScript file using our <a href="https://github.com/filamentgroup/loadJS">loadJS</a> utility. LoadJS fetches files asynchronously (without blocking page rendering), so by using the entire enhancement pattern in this way, the user interface could briefly appear more enhanced than it functionally really is… that is, until the JavaScript loads and executes properly.</p>
<p>Now, this optimism is risky, but it’s useful for an important reason: by applying the CSS for the enhanced version of our UI right away, we avoid the possibility of the UI first rendering in its more basic presentation before flipping into a more enhanced state when the script finishes loading, which tends to look pretty janky. Also, rendering the enhanced UI as soon as we can allows the user to start visually digesting the page content sooner.</p>
<p>Of course, sometimes optimism can come back to bite you. A quick tangent, then back to the code…</p>
<h2 id="a-hostile-medium">A Hostile Medium <a class="direct-link" href="#a-hostile-medium" aria-hidden="true">#</a></h2>
<p>As a web user, these sorts look-before-you-leap of coding patterns have saved me many times, but admittedly, they tend to most benefit folks who are browsing in less-than-ideal conditions, which we tech-priveleged web designers probably do less often than the typical web user. We talk about the real potential for file requests to hang, get blocked, or sometimes just fail to either load or apply, but more often than not, things work well for us and it’s easy to forget to properly plan for fault-tolerance when things aren’t working as perfectly as we’d like.</p>
<p>Interestly, ad and content blockers have brought these failure cases much closer to home, as their very purpose is to inflict user control over the sorts of code that can be transferred to, or executed in, their device, blocking fonts, ads, or even JavaScript itself through a flip of a switch. For a while now, I’ve been browsing with the <a href="https://www.purify-app.com/">Purify</a> content blocker enabled on my iPhone. I installed Purify because it was the most popular app (not just the most popular content blocker, but app in general!) in the Apple App Store at the time, and I wanted to see how it would either improve or degrade my browsing experience, hopefully speeding up performance and minimizing time spent looking at loading spinners. I also wanted to install a content blocker because as a web developer, they represent an increasingly popular use case that I knew I’d need to consider in my development practices. Maybe I would learn that I could be building more robustly than I already was. So I flipped all the main switches on: Block all the things… in this case, ads, fonts, and JavaScript.</p>
<p>With Purify enabled, I expected to find a mixed bag of functional and broken pages on the wider web, and I sure did. When encountering a broken site, I could then decide to open Purify’s settings and “whitelist” that site, or just browse elsewhere (if I really liked the site, I’d do the former). Interestingly, a site that fails to load due to content blocking appears like it’s just plain “down” or busted… and I have a hunch the average content-blocker-user may not know enough to discern the difference and do the work of whitelisting the site. Developers take note!</p>
<p>Anyway, among the sites that didn’t quite work as they should was… gasp, Filament Group’s own website! In our case, some collapsible menus—including our primary navigation on interior pages—were displaying a mixed state: enhanced in appearance, but not functional because the JavaScript that controls them had failed to load due to a blocker. How could that be, I thought, when we’re so careful about these things? Indeed, our enhancement pattern appeared to be a bit too optimistic.</p>
<h2 id="weighing-options">Weighing Options <a class="direct-link" href="#weighing-options" aria-hidden="true">#</a></h2>
<p>Fortunately for Filament’s site, the problem didn’t render the entire site unusable, but it did require the user to navigate to our homepage before choosing another section of the site to browse—very much not ideal. Of course, in sites that are more highly functional than Filament’s, the problem could be much more severe. A fix was definitely needed.</p>
<p>One obvious option was to ditch the early <code>.enhanced</code> class addition and simply let the external JavaScript file apply that class whenever it loads. This would fix the problem, but as I mentioned earlier, it would create a new one as well: users would see the page in its basic presentation first, then the page would re-render in its enhanced state when the script finished loading. If this took any significant amount of time, it would be pretty undesirable.</p>
<p>Another option was to stick with our current pattern and apply some fault tolerance, or dare I say, graceful degradation to accommodate this case should it ever arise.</p>
<p>We ended up taking second approach. Here’s how it works.</p>
<h2 id="cutting-the-mustard%2C-with-takebacksies">Cutting the Mustard, with takebacksies <a class="direct-link" href="#cutting-the-mustard%2C-with-takebacksies" aria-hidden="true">#</a></h2>
<p>Most of the time, our pattern worked great, so we wanted to keep it as-is. But we also wanted to handle the case where things go wrong in a tolerant manner, so that the page would at least be usable. Unfortunately (amazingly!), we don’t have a reliable way to detect if the file fails to load, like we would with say, the <code>onerror</code> event. But, by applying a <code>load</code> event listener to our JavaScript file, we could at least keep tabs on whether that file ever does successfully load. As it turns out, combining a <code>load</code> listener with a reasonable timeout should be enough to handle this responsibly. Here we go…</p>
<p>Again, here is our pattern as it stands:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">if</span><span class="token punctuation">(</span> <span class="token string">"querySelector"</span> <span class="token keyword">in</span> window<span class="token punctuation">.</span>document <span class="token operator">&amp;&amp;</span> <span class="token string">"addEventListener"</span> <span class="token keyword">in</span> window <span class="token punctuation">)</span><span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">// This is a capable browser, let's improve the UI further!</span></span><br><span class="highlight-line"> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className <span class="token operator">+=</span> <span class="token string">" enhanced"</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// load the enhanced scripting</span></span><br><span class="highlight-line"> <span class="token function">loadJS</span><span class="token punctuation">(</span> <span class="token string">"/path/to/enhancements.js"</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Now let’s walk through some small changes to make it more bulletproof.</p>
<p>First, we want to decide a reasonable amount of loading time after which we can give up on the enhancements and just show a usable, basic experience. In our case, to degrade the UI all we have to do is remove that <code>enhanced</code> class from the <code>html</code> element. In debating a timeout, we considered that our site tends to be usable in under 2 seconds on a 3G or better connection, so we landed on a generous timeout of 8 seconds for the time at which we’d assume that something went wrong loading the file. Using a <code>setTimeout</code> function, we can do just that:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><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><br><span class="highlight-line"> <span class="token comment">// remove the enhanced class</span></span><br><span class="highlight-line"> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className <span class="token operator">=</span> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span> <span class="token string">" enhanced"</span><span class="token punctuation">,</span> <span class="token string">""</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">8000</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></span></code></pre>
<p>…and now, with that added to our full example:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">if</span><span class="token punctuation">(</span> <span class="token string">"querySelector"</span> <span class="token keyword">in</span> window<span class="token punctuation">.</span>document <span class="token operator">&amp;&amp;</span> <span class="token string">"addEventListener"</span> <span class="token keyword">in</span> window <span class="token punctuation">)</span><span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">// This is a capable browser, let's improve the UI further!</span></span><br><span class="highlight-line"> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className <span class="token operator">+=</span> <span class="token string">" enhanced"</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// load the enhanced scripting</span></span><br><span class="highlight-line"> <span class="token function">loadJS</span><span class="token punctuation">(</span> <span class="token string">"/path/to/enhancements.js"</span> <span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// set a timeout to degrade the ui after 8 seconds</span></span><br><span class="highlight-line"> <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><br><span class="highlight-line"> <span class="token comment">// remove the enhanced class</span></span><br><span class="highlight-line"> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className <span class="token operator">=</span> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span> <span class="token string">" enhanced"</span><span class="token punctuation">,</span> <span class="token string">""</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">8000</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Nice, that works great. But we’re not done yet. After all, that JavaScript file could still end up loading successfully, and if that happens, we’ll run into an entirely different mixed UI problem, where the behavior is applied but the presentation is not.</p>
<p>As I’d noted before, we can’t cancel that script from loading, but we can listen to see if it does load. By applying a <code>load</code> event listener, we could re-enhance the UI if that script ever does end up loading, long after we’ve already left it for dead.</p>
<p>The <code>loadJS</code> function returns a reference to the script we’re loading, so we can assign a variable to that call and bind event listeners to it after that.</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token comment">// load the enhanced scripting</span></span><br><span class="highlight-line"><span class="token keyword">var</span> script <span class="token operator">=</span> <span class="token function">loadJS</span><span class="token punctuation">(</span> <span class="token string">"/path/to/enhancements.js"</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"></span><br><span class="highlight-line"><span class="token comment">// when the script loads, make sure that class is present still</span></span><br><span class="highlight-line">script<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></span><br><span class="highlight-line"> <span class="token comment">// add this class, just in case it was removed already (we can't cancel this request so it might arrive any time)</span></span><br><span class="highlight-line"> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className <span class="token operator">+=</span> <span class="token string">" enhanced"</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span><span class="token punctuation">;</span></span></code></pre>
<p>…and again with the full example:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">if</span><span class="token punctuation">(</span> <span class="token string">"querySelector"</span> <span class="token keyword">in</span> window<span class="token punctuation">.</span>document <span class="token operator">&amp;&amp;</span> <span class="token string">"addEventListener"</span> <span class="token keyword">in</span> window <span class="token punctuation">)</span><span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">// This is a capable browser, let's improve the UI further!</span></span><br><span class="highlight-line"> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className <span class="token operator">+=</span> <span class="token string">" enhanced"</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// load the enhanced scripting</span></span><br><span class="highlight-line"> <span class="token keyword">var</span> script <span class="token operator">=</span> <span class="token function">loadJS</span><span class="token punctuation">(</span> <span class="token string">"/path/to/enhancements.js"</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// set a timeout to degrade the ui after 8 seconds</span></span><br><span class="highlight-line"> <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><br><span class="highlight-line"> <span class="token comment">// remove the enhanced class</span></span><br><span class="highlight-line"> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className <span class="token operator">=</span> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span> <span class="token string">" enhanced"</span><span class="token punctuation">,</span> <span class="token string">""</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">8000</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// when the script loads, make sure that class is present still</span></span><br><span class="highlight-line"> script<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></span><br><span class="highlight-line"> <span class="token comment">// add this class, just in case it was removed already (we can't cancel this request so it might arrive any time)</span></span><br><span class="highlight-line"> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className <span class="token operator">+=</span> <span class="token string">" enhanced"</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Whew… lotta logic for a small bit of code. Alas, we’re not quite done. That’s because in most cases, that JavaScript file will load very quickly—much sooner than our 8 second limit—but then the timer is still running and after 8 seconds, the UI will be degraded to a mixed, unusable state! So lastly, our script’s <code>load</code> handler needs to cancel that timer so that it never ends up executing its callback function. We can do that by assigning a variable to the timer, allowing us to clear it whenever we want.</p>
<p>Here’s that <code>fallback</code> variable in play, both in setting the timer, and clearing it if the script loads:</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">if</span><span class="token punctuation">(</span> <span class="token string">"querySelector"</span> <span class="token keyword">in</span> window<span class="token punctuation">.</span>document <span class="token operator">&amp;&amp;</span> <span class="token string">"addEventListener"</span> <span class="token keyword">in</span> window <span class="token punctuation">)</span><span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">// This is a capable browser, let's improve the UI further!</span></span><br><span class="highlight-line"> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className <span class="token operator">+=</span> <span class="token string">" enhanced"</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// load the enhanced scripting</span></span><br><span class="highlight-line"> <span class="token keyword">var</span> script <span class="token operator">=</span> <span class="token function">loadJS</span><span class="token punctuation">(</span> <span class="token string">"/path/to/enhancements.js"</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// if script hasn't loaded after 8 seconds, remove the enhanced class</span></span><br><span class="highlight-line"> <span class="token keyword">var</span> fallback <span class="token operator">=</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><br><span class="highlight-line"> <span class="token comment">// remove the enhanced class</span></span><br><span class="highlight-line"> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className <span class="token operator">=</span> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span> <span class="token string">" enhanced"</span><span class="token punctuation">,</span> <span class="token string">""</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">8000</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// when the script loads, clear the timer out and add the class again just in case</span></span><br><span class="highlight-line"> script<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></span><br><span class="highlight-line"> <span class="token comment">// clear the fallback timer</span></span><br><span class="highlight-line"> <span class="token function">clearTimeout</span><span class="token punctuation">(</span> fallback <span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br><span class="highlight-line"> <span class="token comment">// add this class, just in case it was removed already (we can't cancel this request so it might arrive any time)</span></span><br><span class="highlight-line"> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>className <span class="token operator">+=</span> <span class="token string">" enhanced"</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>And that’s it! Of course, the script above could be refactored a little bit to improve its ease of maintenance. How’s this instead?</p>
<pre class="language-js"><code class="language-js"><span class="highlight-line"><span class="token keyword">if</span><span class="token punctuation">(</span> <span class="token string">"querySelector"</span> <span class="token keyword">in</span> window<span class="token punctuation">.</span>document <span class="token operator">&amp;&amp;</span> <span class="token string">"addEventListener"</span> <span class="token keyword">in</span> window <span class="token punctuation">)</span><span class="token punctuation">{</span></span><br><span class="highlight-line"> <span class="token comment">// This is a capable browser, let's improve the UI further!</span></span><br><span class="highlight-line"> <span class="token keyword">var</span> docElem <span class="token operator">=</span> window<span class="token punctuation">.</span>document<span class="token punctuation">.</span>documentElement<span class="token punctuation">;</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// the class we'll use to enhance the UI</span></span><br><span class="highlight-line"> <span class="token keyword">var</span> enhancedClass <span class="token operator">=</span> <span class="token string">"enhanced"</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token keyword">var</span> enhancedScriptPath <span class="token operator">=</span> <span class="token string">"/path/to/enhancements.js"</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// add enhanced class</span></span><br><span class="highlight-line"> <span class="token keyword">function</span> <span class="token function">addClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span></span><br><span class="highlight-line"> docElem<span class="token punctuation">.</span>className <span class="token operator">+=</span> <span class="token string">" "</span> <span class="token operator">+</span> enhancedClass<span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token punctuation">}</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// remove enhanced class</span></span><br><span class="highlight-line"> <span class="token keyword">function</span> <span class="token function">removeClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span></span><br><span class="highlight-line"> docElem<span class="token punctuation">.</span>className <span class="token operator">=</span> docElem<span class="token punctuation">.</span>className<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span> enhancedClass<span class="token punctuation">,</span> <span class="token string">" "</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token punctuation">}</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// Let's enhance optimistically...</span></span><br><span class="highlight-line"> <span class="token function">addClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// load enhanced JS file</span></span><br><span class="highlight-line"> <span class="token keyword">var</span> script <span class="token operator">=</span> <span class="token function">loadJS</span><span class="token punctuation">(</span> enhancedScriptPath <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// if script hasn't loaded after 8 seconds, remove the enhanced class</span></span><br><span class="highlight-line"> <span class="token keyword">var</span> fallback <span class="token operator">=</span> <span class="token function">setTimeout</span><span class="token punctuation">(</span> removeClass<span class="token punctuation">,</span> <span class="token number">8000</span> <span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> </span><br><span class="highlight-line"> <span class="token comment">// when the script loads, clear the timer out and add the class again just in case</span></span><br><span class="highlight-line"> script<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></span><br><span class="highlight-line"> <span class="token comment">// clear the fallback timer</span></span><br><span class="highlight-line"> <span class="token function">clearTimeout</span><span class="token punctuation">(</span> fallback <span class="token punctuation">)</span><span class="token punctuation">;</span> </span><br><span class="highlight-line"> <span class="token comment">// add this class, just in case it was removed already (we can't cancel this request so it might arrive any time)</span></span><br><span class="highlight-line"> <span class="token function">addClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></span><br><span class="highlight-line"> <span class="token punctuation">}</span><span class="token punctuation">;</span></span><br><span class="highlight-line"><span class="token punctuation">}</span></span></code></pre>
<p>Now THAT cuts the mustard! (Sorry…)</p>
<p>Anyway, visit any Filament Group interior page and bathe in the resilience! And of course, I should note that this approach is not purely about accommodating content blockers, but about better handling any case where a script fails to load (<em>ever used the wifi on an Amtrak?</em>). We’d love if you do the same on your site (seriously, I’m trying to use that thing! ;) ).</p>
<p>Thanks for reading, and do hit us up on <a href="http://twitter.com/filamentgroup">twitter</a> with any feedback or questions you may have.</p>
Delivering Responsibly2015-10-26T00:00:00-04:00<h1>Delivering Responsibly</h1>
<p>Posted by Scott on 10/26/2015</p>
<p>At several conferences throughout the past year, I’ve presented a talk called <em>Delivering Responsibly</em> with slight variations and updates each time. The following is a transcript and slides from the last time I gave the presentation, at <a href="https://fronteers.nl/congres/2015">Fronteers Conference</a> in Amsterdam, October 2015.</p>
<hr>
<figure>
<img src="/images/delivering-responsibly/400/01.jpg" srcset="/images/delivering-responsibly/400/01.jpg 400w, /images/delivering-responsibly/800/01.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="Cover slide: Delivering Responsibly. Scott Jehl, Filament Group.">
</figure>
<blockquote>
<p>Today I’m going to talk about delivering fast, resilient, accessible sites.
I’ll cover some challenges we face in doing that, and some practices and upcoming standards that we can use to make sites usable on any device as soon as possible.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/02.jpg" srcset="/images/delivering-responsibly/400/02.jpg 400w, /images/delivering-responsibly/800/02.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>02: Responding</figcaption>
</figure>
<blockquote>
<p>As web designers and developers, we all have our own specialties and responsibilities, but broadly speaking, collectively, we might say it is our job is to deliver sites that respond to our users’ needs.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/03.jpg" srcset="/images/delivering-responsibly/400/03.jpg 400w, /images/delivering-responsibly/800/03.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>03: People testing a variety of devices.</figcaption>
</figure>
<blockquote>
<p>Sounds simple enough.</p>
</blockquote>
<blockquote>
<p>But if you’ve built sites that work across many devices before you know that it’s anything but simple. It’s been shown that our users’ requests are largely the same regardless of their browsing situation, but the way we <em>respond</em> to each user’s requests can <em>and should</em> vary dramatically. That’s because there’s incredible diversity in the means by which people access our sites.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/04.jpg" srcset="/images/delivering-responsibly/400/04.jpg 400w, /images/delivering-responsibly/800/04.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>04: Variety of devices on a table. - <a href="https://www.flickr.com/photos/adactio/6153522068/">Adactio on Flickr</a></figcaption>
</figure>
<blockquote>
<p>Ultimately, we want to deliver services that feel at home and <em>appropriate</em> on any given device. And on the web today, our sites need to respond <em>appropriately</em> to an increasing number of factors.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/05.jpg" srcset="/images/delivering-responsibly/400/05.jpg 400w, /images/delivering-responsibly/800/05.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>05: A Hostile Medium</figcaption>
</figure>
<blockquote>
<p>It’s been said that the web is a hostile medium to work with. I think it’s especially hostile to assumptions–assumptions that users, browsers or networks are certain to behave in a particular way, or have certain characteristics.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/06.jpg" srcset="/images/delivering-responsibly/400/06.jpg 400w, /images/delivering-responsibly/800/06.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>06: “The web is not a platform.It’s a continuum.” - <a href="https://adactio.com/journal/6692">@adactio</a> </figcaption>
</figure>
<blockquote>
<p>That’s because the web isn’t any one platform, but more of a continuum, to quote Jeremy Keith. A continuum of features and constraints.</p>
</blockquote>
<blockquote>
<p>Or, if that sounds overly tidy, you might say it’s more of a scatterplot.</p>
</blockquote>
<blockquote>
<p>There’s so much to consider. Let’s talk about some of those features and constraints.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/07.jpg" srcset="/images/delivering-responsibly/400/07.jpg 400w, /images/delivering-responsibly/800/07.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>07: Photo of a device lab. - <a href="https://www.flickr.com/photos/adactio/14535705876">Adactio on Flickr</a></figcaption>
</figure>
<blockquote>
<p>Perhaps most obviously, our interfaces need to respond appropriately to a device’s viewport size. And there’s an incredible array of viewport sizes to consider today.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/08.jpg" srcset="/images/delivering-responsibly/400/08.jpg 400w, /images/delivering-responsibly/800/08.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>08: Viewport sizes overlaid. - [@lukew](https://twitter.com/lukew/status/542002569392844801)</figcaption>
</figure>
<blockquote>
<p>Across devices, viewport sizes have reached a nearly continuous gradient from watches to phones to e-readers to tablets to laptops to monitors to tvs, and more. Many devices offer two orientations as well. Some can browse in split screen. It’s almost silly to focus on any particular viewport size anymore because they can be almost anything.</p>
</blockquote>
<blockquote>
<p>This calls for designing fluid user interfaces, rather than toggling between a set of fixed-sized designs, and responsive design allows us to do just that.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/09.jpg" srcset="/images/delivering-responsibly/400/09.jpg 400w, /images/delivering-responsibly/800/09.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>09: The same interface shown in different states of enhancement. </figcaption>
</figure>
<blockquote>
<p>But there’s a lot more. Along with viewport size differences, we need to pair the fidelity of our interfaces with that which can be expected of a particular device’s capabilities.</p>
</blockquote>
<blockquote>
<p>This means qualifying the application of CSS and JS to ensure UI enhancements apply safely, to avoid breaking an already usable experience.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/10.jpg" srcset="/images/delivering-responsibly/400/10.jpg 400w, /images/delivering-responsibly/800/10.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>10: Opera Mini browser</figcaption>
</figure>
<blockquote>
<p>Feature support differences happen for all kinds of reasons, not just by accident and not just in older browsers.</p>
</blockquote>
<blockquote>
<p>For example, millions of people use proxy browsers like Opera Mini. It’s a great browser, especially if you have a simpler phone or want to be conscious of your data usage.</p>
</blockquote>
<blockquote>
<p>“Mini” proxies everything you request to compress it and make it transfer quickly and cheaply. It also probably breaks your JavaScript, and doesn’t support some commonly-used workarounds, like icon fonts.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/11.jpg" srcset="/images/delivering-responsibly/400/11.jpg 400w, /images/delivering-responsibly/800/11.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>11: An iOS device with the VoiceOver dial gesture being used to navigate a site.</figcaption>
</figure>
<blockquote>
<p>“Mini” is just one example of how feature support can vary, but subtler feature variations occur across all browsers and devices.</p>
</blockquote>
<blockquote>
<p>Input and output mechanisms vary too. Most devices offer multiple means of interacting with sites, between hardware buttons and software gestures, mouse, keyboard, and assistive technology.</p>
</blockquote>
<blockquote>
<p>We must be careful not to assume that any one of those modes will be exclusively in play. Factors like screen size and say, support for touch interaction do not always correlate anymore.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/12.jpg" srcset="/images/delivering-responsibly/400/12.jpg 400w, /images/delivering-responsibly/800/12.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>12: People using smartphones on a train. - <a href="https://www.flickr.com/photos/wolfganglin/8028028088">Adactio on Flickr</a></figcaption>
</figure>
<blockquote>
<p>Those were some device and UI-related conditions to consider. But mobile devices brought other constraints to the web too, like widely varying network behavior.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/13.jpg" srcset="/images/delivering-responsibly/400/13.jpg 400w, /images/delivering-responsibly/800/13.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>13: “60% of the world’s connections are sub-3G.” [PDF](http://www.ericsson.com/res/docs/2013/ericsson-mobility-report-june-2013.pdf)</figcaption>
</figure>
<blockquote>
<p>According to Ericsson’s Mobility Report in 2013, just 2 years ago, 60% of the world’s connections were sub-3G.</p>
</blockquote>
<blockquote>
<p>To be clear, that’s 2G–“Edge”–as the majority network speed worldwide.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/14.jpg" srcset="/images/delivering-responsibly/400/14.jpg 400w, /images/delivering-responsibly/800/14.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>14: Figures from Ericsson’s Mobility Report, 2015. [PDF](http://www.ericsson.com/res/docs/2015/ericsson-mobility-report-june-2015.pdf)</figcaption>
</figure>
<blockquote>
<p>And today, that 2G and 3G majority is still very much here.</p>
</blockquote>
<blockquote>
<p>This is from Ericsson’s 2015 report. Focus on the green areas: that’s 4G coverage. The US leads in 4G support at 40%, but it still has more users on 3G and slower. Western Europe is still 60% 3G, with only 15% 4G.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/15.jpg" srcset="/images/delivering-responsibly/400/15.jpg 400w, /images/delivering-responsibly/800/15.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>15: 2015 report with 2020 projections added.</figcaption>
</figure>
<blockquote>
<p>Ericcson’s projections for 2020 are encouraging… that is, if you only aim to reach folks in the US and Western Europe.</p>
</blockquote>
<blockquote>
<p>Those green bars are going to take a while to fill in.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/16.jpg" srcset="/images/delivering-responsibly/400/16.jpg 400w, /images/delivering-responsibly/800/16.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>16: People charging smartphones. [Reuters photo](http://mobile.reuters.com/article/topNews/idUSKCN0Q912620150804?irpc=932)</figcaption>
</figure>
<blockquote>
<p>So great network speed isn’t evenly distributed yet. But <em>speed</em> isn’t our only network concern.</p>
</blockquote>
<blockquote>
<p>Network reliability and consistency have huge impacts on how we deliver sites, as HTTP requests come with no delivery guarantee. They can <em>and do</em> drop, hang, or time out.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/17.jpg" srcset="/images/delivering-responsibly/400/17.jpg 400w, /images/delivering-responsibly/800/17.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>17: “Google services like fonts, translate and hosted JS are 100% blocked in China. Sites that use them are slow/broken.” [- @thebenedict](https://twitter.com/thebenedict/status/623725837020450816)</figcaption>
</figure>
<blockquote>
<p>Requests can be blocked deliberately too, like in China where all of Google’s services are blocked.</p>
</blockquote>
<blockquote>
<p>Google fonts, their JavaScript library CDNS… all blocked in China. So sites that depend on those services working for them may not work at all there.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/18.jpg" srcset="/images/delivering-responsibly/400/18.jpg 400w, /images/delivering-responsibly/800/18.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>18: Ad & Tracking Blockers: Privacy Badger, Adblock Plus, uBlock Origin</figcaption>
</figure>
<blockquote>
<p>Beyond that, even on a reliable and unimpeded connection, requests can fail for other, more personalized reasons.</p>
</blockquote>
<blockquote>
<p>We often say that Progressive Enhancement is not about people who disable their javascript (because who would do that, right?).</p>
</blockquote>
<blockquote>
<p>Well, we might want to relax that position a bit. Recently, we’re seeing new and very mainstream ways that many browser features can be blocked or disabled. The most popular Ad blockers often block commonly used CDNs by default, like the ones we use to host our CSS and JavaScript. So our enhancements may or may not reach our users.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/19.jpg" srcset="/images/delivering-responsibly/400/19.jpg 400w, /images/delivering-responsibly/800/19.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>19: iOS9 Content Blockers: Purify, Peace, </figcaption>
</figure>
<blockquote>
<p>Content Blockers, which are new to iOS9, allow users to block types of assets</p>
</blockquote>
<blockquote>
<p>This screencap is from one of the most popular apps in the app store today, Purify. The entire app is just a panel that offers a few major features to disable to speed up the Safari browser, like custom fonts, JavaScript, trackers, ads, images. Maybe disabling JavaScript or custom fonts is already a lot more common than we think.</p>
</blockquote>
<blockquote>
<p>Also, content blockers can <em>block</em> javascript requests, but they don’t <em>disable</em> JavaScript. So noscript fallbacks won’t help in this case – requests simply fail.</p>
</blockquote>
<blockquote>
<p>There are other side effects to consider here. People have come to learn that web fonts take a long time to load. Boom! Just disable them. And along with custom fonts, there go the icon fonts too, so we need to be thinking about fallback images and text for font icons. Or better yet, use SVG instead.</p>
</blockquote>
<blockquote>
<p>Most importantly, if disabling enhancements like JavaScript or ads ends up breaking your site, will people just assume that your site is down? I suspect they will. I would.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/20.jpg" srcset="/images/delivering-responsibly/400/20.jpg 400w, /images/delivering-responsibly/800/20.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>20: [NYTimes study of Mobile Ads](http://www.nytimes.com/interactive/2015/10/01/business/cost-of-mobile-ads.html?_r=0)</figcaption>
</figure>
<blockquote>
<p>And really who would blame people for reacting to the web this way? Our own practices have set the stage for blockers to become enormously popular.</p>
</blockquote>
<blockquote>
<p>The NYTimes recently did a story and found more than 50% of all mobile data comes from ads.</p>
</blockquote>
<blockquote>
<p>…they also ran a story on iOS content blockers and how they can help speed up your browser, so you can bet that real people are using them <em>(not just web developers)</em>.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/21.jpg" srcset="/images/delivering-responsibly/400/21.jpg 400w, /images/delivering-responsibly/800/21.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>21: Woman with a smartphone. [Flickr](https://www.flickr.com/photos/johnragai/15823751904)</figcaption>
</figure>
<blockquote>
<p>So these factors collectively form a pretty hostile medium. But it’s important to note that these are not just factors that make our jobs harder. Considering these factors IS our job. Our job is to use the strengths of our medium to reach people and respond to their needs.</p>
</blockquote>
<blockquote>
<p>And I think reach is the greatest advantage of web technology. If we do our jobs well, our sites can reach folks who access the web under very different circumstances than many of we web designers do day to day.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/22.jpg" srcset="/images/delivering-responsibly/400/22.jpg 400w, /images/delivering-responsibly/800/22.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>22: Not just a matter of empathy. Access is our job.</figcaption>
</figure>
<blockquote>
<p>…and that’s inspiring I think. But I find that despite that we have this distinct advantage in using web technology–that it can have enormous reach–it’s common to encounter sites that are built in ways that inhibit their reach… that aren’t resilient.</p>
</blockquote>
<blockquote>
<p>Sometimes that’s due to assumptions we web developers make about our users, or conveniences we desire in our workflow.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/23.jpg" srcset="/images/delivering-responsibly/400/23.jpg 400w, /images/delivering-responsibly/800/23.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>23: A house blueprint [Flickr](https://www.flickr.com/photos/instantjefferson/2231404)</figcaption>
</figure>
<blockquote>
<p>Some of our most popular web frameworks are built on very risky assumptions. Imagine a house purposefully built so that you could not enter it if the power goes out. Even if all you need is shelter.</p>
</blockquote>
<blockquote>
<p>That sounds crazy, but that’s exactly how a lot of web apps base their reliance on JavaScript loading and running. A site’s content &amp; basic function is often reasonable to offer without using any JavaScript at all, yet we sometimes choose to make our apps entirely reliant on the web’s most fragile layer.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/24.jpg" srcset="/images/delivering-responsibly/400/24.jpg 400w, /images/delivering-responsibly/800/24.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>24: Web developers working together. [Flickr](https://www.flickr.com/photos/lacantine/6234723672/)</figcaption>
</figure>
<blockquote>
<p>It’s easy for us to forget about that fragility.</p>
</blockquote>
<blockquote>
<p>As developers and designers, many of us work in relatively ideal conditions. To work efficiently, we need fast, reliable networks to stream enormous amounts of data, and we need to have access to the latest, most capable internet-accessing devices in existence.</p>
</blockquote>
<blockquote>
<p>So it’s easy to forget that we’re often an edge case amongst our own users, and potential users.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/25.jpg" srcset="/images/delivering-responsibly/400/25.jpg 400w, /images/delivering-responsibly/800/25.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>25: Average Webpage Weight: 2.1mb. -[httparchive.org](http://httparchive.org/interesting.php?a=All&l=Sep%2015%202015)</figcaption>
</figure>
<blockquote>
<p>And when we forget, very real costs are transferred on.</p>
</blockquote>
<blockquote>
<p>Today the average webpage weighs over 2mb. I’ve had this chart in my slide decks now for 3 years. In 2012 the number was just over 1mb, and we were upset about it then too.</p>
</blockquote>
<blockquote>
<p>This figure came from the top 10,000 sites on the web, but it may even be optimistic. Many popular sites today are heavier than 2mb. And that’s a real problem because most devices today access the web over data plans that have monthly or prepaid caps</p>
</blockquote>
<blockquote>
<p>There’s a real cost to every byte we download.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/26.jpg" srcset="/images/delivering-responsibly/400/26.jpg 400w, /images/delivering-responsibly/800/26.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>26: [What Does My Site Cost?](http://whatdoesmysitecost.com)</figcaption>
</figure>
<blockquote>
<p>Tim Kadlec recently built <a href="http://whatdoesmysitecost.com">What Does My Site Cost .com</a>. It’s a website that calculates the real cost of accessing any site on the web using the costs of the cheapest data plans around the world.</p>
</blockquote>
<blockquote>
<p>For example, <a href="http://www.wired.com/2015/03/our-new-site/">an article on the Wired site</a> weighs over 11.27 mb. For some people it costs almost $4 US dollars to visit that page! For many it’s at least a dollar.</p>
</blockquote>
<blockquote>
<p>So page weight does matter. Access can be slow, expensive and prohibitive.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/27.jpg" srcset="/images/delivering-responsibly/400/27.jpg 400w, /images/delivering-responsibly/800/27.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>27: Simulating Real Life</figcaption>
</figure>
<blockquote>
<p>And besides, building sites that only work for the luckiest, most fortunate users on the web is not just bad for business, it’s also a pretty boring use of our technology. The most interesting challege of the web is the building something that can reach and connect more people than any other technology can dream of reaching.</p>
</blockquote>
<blockquote>
<p>So rather than designing for the best case scenario, we need to design for reality, because our users live in the real world. But how do we, the privileged web developers, know what it’s really like out there?!</p>
</blockquote>
<blockquote>
<p>Tools! We have it so good, we need to use tools that simulate what the real world is like for our users. Here are a few of my favorites.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/28.jpg" srcset="/images/delivering-responsibly/400/28.jpg 400w, /images/delivering-responsibly/800/28.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>28: Chrome's Devtools</figcaption>
</figure>
<blockquote>
<p>First, there are network and device emulation tools built right into our browsers’ devtools. This is Chrome devtools with the device panel open to select a network throttling speed. All of the browser’s requests go through that throttle when it’s in play, so you can test how things look on a 3G connection, for example. Again, the most popular speed in the world.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/29.jpg" srcset="/images/delivering-responsibly/400/29.jpg 400w, /images/delivering-responsibly/800/29.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>29: [Network Link Conditioner](https://developer.apple.com/downloads/?name=Hardware%20IO%20Tools%20for%20Xcode%207)</figcaption>
</figure>
<blockquote>
<p>If you want to throttle network speeds in other browsers, we have options there too. This is a Mac preference pane called Network Link Conditioner, which you can get from the app store.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/30.jpg" srcset="/images/delivering-responsibly/400/30.jpg 400w, /images/delivering-responsibly/800/30.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>30: [Comcast network tool](https://github.com/tylertreat/comcast)</figcaption>
</figure>
<blockquote>
<p>There’s also this other network throttling tool, which was apparently named by an elite-level troll.</p>
</blockquote>
<blockquote>
<p>“Comcast” is a tool that simulates, uh, not good connections–like those you might experience if you get your internet through Comcast in the US. Everyone loves to hate Comcast apparently.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/31.jpg" srcset="/images/delivering-responsibly/400/31.jpg 400w, /images/delivering-responsibly/800/31.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>31: Ad and Content Blockers</figcaption>
</figure>
<blockquote>
<p>Another way we can simulate real life conditions is by testing with extensions and blockers.</p>
</blockquote>
<blockquote>
<p>Put yourself in your users’ shoes. Disable things. Disable JavaScript, disable fonts. Try to break your site, then fix it! Think of your site as a tool that needs to work in adverse conditions, because this is how your site is actually going to be used.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/32.jpg" srcset="/images/delivering-responsibly/400/32.jpg 400w, /images/delivering-responsibly/800/32.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>32: <a href="http://browserstack.com">Browserstack</a></figcaption>
</figure>
<blockquote>
<p>On the browser &amp; device testing side of things, we have tools like Browserstack. Browserstack has live device testing–not just static screenshots–and often you’re using real devices on BrowserStack, not just emulators (which are helpful as well).</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/33.jpg" srcset="/images/delivering-responsibly/400/33.jpg 400w, /images/delivering-responsibly/800/33.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>33: Devices. [Flickr](https://www.flickr.com/photos/jfingas/10104822523/)</figcaption>
</figure>
<blockquote>
<p>Of course, nothing replaces testing a real device in your own hands. For performance, animations, touch gestures, don’t trust an emulator or virtual service alone. Get yourself some devices.</p>
</blockquote>
<blockquote>
<p>I’d recommend searching Amazon to find out what’s most new and popular–test on those. Some of the most popular phones on Amazon are very basic smartphones with pretty rudimentary rendering.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/34.jpg" srcset="/images/delivering-responsibly/400/34.jpg 400w, /images/delivering-responsibly/800/34.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>34: [webpagetest.org](http://webpagetest.org)</figcaption>
</figure>
<blockquote>
<p>Lastly, the tool I’d recommend most is <a href="http://webpagetest.org">webpagetest.org</a>. It’s a website. You can enter a URL, choose a browser/device combination to test, and a region of the world to run the test from, and webpagetest will load your page from there and give you all sorts of information about how it loaded. It’s my favorite tool for development and testing.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/35.jpg" srcset="/images/delivering-responsibly/400/35.jpg 400w, /images/delivering-responsibly/800/35.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>35: Testing on mobile devices. - <a href="https://www.flickr.com/photos/adactio/12674596614">Adactio on Flickr</a></figcaption>
</figure>
<blockquote>
<p>Simulating the real world is critical. We need to consider functional, manual testing to be a major portion of our development cycle. It’s not just something to throw over to QA when we’re done. Use these tools as you develop to make things bulletproof.</p>
</blockquote>
<blockquote>
<p>I find that sometimes, writing the code takes less time than testing it and hardening it. That’s probably just something to expect.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/36.jpg" srcset="/images/delivering-responsibly/400/36.jpg 400w, /images/delivering-responsibly/800/36.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>36: Delivering Quickly</figcaption>
</figure>
<blockquote>
<p>So I’ve identified some of the factors we need to consider, and ways we can produce less-than ideal conditions at home. To thrive in a hostile, unpredictable, undependable environment, we need to build for resilience.</p>
</blockquote>
<blockquote>
<p>Right now may the most exciting and challenging time in the web’s history to be building fault-tolerant, resilient sites. So for the remainder of this talk, I’m going to focus on delivery, because delivering and responding quickly is the first step to a broadly accessible site.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/37.jpg" srcset="/images/delivering-responsibly/400/37.jpg 400w, /images/delivering-responsibly/800/37.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>37: A desert crossroads [Flickr](https://www.flickr.com/photos/97423979@N00)</figcaption>
</figure>
<blockquote>
<p>From a technical perspective, right now is a very unique time to be building on the web. We’re at a bit of a technological crossroads.</p>
</blockquote>
<blockquote>
<p>On the one hand, we’ve spent years developing tools and practices that have just begun to prove their potential. We’ve found ways to work around weaknesses in the delivery pipeline.</p>
</blockquote>
<blockquote>
<p>On the other, we have some major infrastructure changes that are just now happening on the web, and those changes make some of our best practices no longer necessary.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/38.jpg" srcset="/images/delivering-responsibly/400/38.jpg 400w, /images/delivering-responsibly/800/38.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>38: A freeway onramp [Flickr](https://www.flickr.com/photos/criminalintent/17321075676)</figcaption>
</figure>
<blockquote>
<p>One new infrastructure change is http/2. Http/2 is not a breaking change, but rather a feature that capable browsers can opt into utilizing. So maybe better metaphor than a crossroads would be an on-ramp, or an escalator.</p>
</blockquote>
<blockquote>
<p>We’ve been running up against barriers in http/1 for a long time now, and we’ve developed some great ways to work within its constraints. In an http/2 world, those workarounds will still work fine, but many won’t be necessary anymore, and won’t be ideal.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/39.jpg" srcset="/images/delivering-responsibly/400/39.jpg 400w, /images/delivering-responsibly/800/39.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>39: Unnecessary in http/2</figcaption>
</figure>
<blockquote>
<p>Http2 negates the need for a lot of things. Like common workarounds that reduce http requests: image sprites, concatenating text files, and inlining.</p>
</blockquote>
<blockquote>
<p>We’ve also done things like hosting our files across many domains to work around limits in the number of active requests we can have on any one domain. And those limits are no longer there anymore in http/2, so we won’t need to do that anymore.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/40.jpg" srcset="/images/delivering-responsibly/400/40.jpg 400w, /images/delivering-responsibly/800/40.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>40: …still necessary</figcaption>
</figure>
<blockquote>
<p>Of course, a great deal of our existing best practices won’t change at all. We still want to transfer as little data as possible to the user. We still want to avoid making unnecessary requests in the first place. We still want CDNs with servers close to our users for faster file transfers. And we still need to worry about network stability and feature diversity across platforms. These problems don’t go away in an http/2 world.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/41.jpg" srcset="/images/delivering-responsibly/400/41.jpg 400w, /images/delivering-responsibly/800/41.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>41: http/2 browser support [caniuse](http://caniuse.com/#search=http2)</figcaption>
</figure>
<blockquote>
<p>Also, like most things on the web, http/2 won’t work in all browsers. IE versions prior to 11 will never have http/2 support, nor will older versions of iOS or Android, and opera mini has no support yet either.</p>
</blockquote>
<blockquote>
<p>But! look at this support already, it’s amazing! Browser support is actually farther ahead than server support for http/2 (most server-side platforms have at least some http/2 features covered now), but both sides are quickly shaping up.</p>
</blockquote>
<blockquote>
<p>We should be factoring http/2 into our workflow today, or at least into our long-term planning.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/42.jpg" srcset="/images/delivering-responsibly/400/42.jpg 400w, /images/delivering-responsibly/800/42.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>42: Lightening our load</figcaption>
</figure>
<blockquote>
<p>We need to prioritize practices that will work today and tomorrow. One such practice is optimizing file size. There are easy and well-known ways we can reduce the weight of our pages and assets.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/43.jpg" srcset="/images/delivering-responsibly/400/43.jpg 400w, /images/delivering-responsibly/800/43.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>43: Easy, classic, performance optimizations: Optimize + Minify + Gzip</figcaption>
</figure>
<blockquote>
<p>To start, we should be optimizing our files so that they’re lighter for network travel. We should make sure our images and fonts are small as can be. We should remove unmeaningful whitespace in text files: css, javascript, even HTML can be minified. And of course, we should compress for transfer with GZIP.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/44.jpg" srcset="/images/delivering-responsibly/400/44.jpg 400w, /images/delivering-responsibly/800/44.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>44: Don’t just optimize images, make them responsive.</figcaption>
</figure>
<blockquote>
<p>In addition to merely optimizing images, it often makes sense to offer different versions of an image based on browser conditions like viewport size, screen resolution, or even network speed. And our community has worked very hard to develop new standards that make this easy for us to manage.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/45.jpg" srcset="/images/delivering-responsibly/400/45.jpg 400w, /images/delivering-responsibly/800/45.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>45: Available now: image standards</figcaption>
</figure>
<blockquote>
<p>Srcset and Picture are new web standards. They’re at least partially supported in most browsers now. Both have nice fallbacks built-in.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/46.jpg" srcset="/images/delivering-responsibly/400/46.jpg 400w, /images/delivering-responsibly/800/46.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>46: [Automating Resource Selection with Client Hints](https://developers.google.com/web/updates/2015/09/automating-resource-selection-with-client-hints)</figcaption>
</figure>
<blockquote>
<p>We also have new features that are exposed to the server in each request the browser makes.</p>
</blockquote>
<blockquote>
<p>Client Hints are a new type of headers we can opt-into sending with requests for things like images, and they let the server decide how best to respond, which means we can alleviate our markup from defining all the potential resources it might need to request.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/47.jpg" srcset="/images/delivering-responsibly/400/47.jpg 400w, /images/delivering-responsibly/800/47.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>47: Coming soon: Client Hints</figcaption>
</figure>
<blockquote>
<p>Here’s how they work. In the markup, we create a meta tag with a few Client Hints to send. Device pixel ratio, viewport width, and rendered asset width (as defined in sizes). And you can see the headers that the server will receive. The server can use this information to return whatever it wants for “mypicture,” meaning your HTML file can have less configuration info. This is a standard that will work in Chrome 46 and Opera, for starters.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/48.jpg" srcset="/images/delivering-responsibly/400/48.jpg 400w, /images/delivering-responsibly/800/48.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>48: Optimizing the Critical Rendering Path</figcaption>
</figure>
<blockquote>
<p>So making our delivery payload smaller helps a lot. Data costs money, and smaller amounts of data transfers faster as well.</p>
</blockquote>
<blockquote>
<p>But beyond file sizes, we also need to think about streamlining our path to rendering the page so it spins up as quickly as possible. And that’s where file size is less of a concern than how we prioritize our asset loading pipeline.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/49.jpg" srcset="/images/delivering-responsibly/400/49.jpg 400w, /images/delivering-responsibly/800/49.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>49: “When it comes to your web browsing experience, it turns out that latency, not bandwidth, is likely the constraining factor today. ” - [Ilya Grigorik](https://www.igvita.com/2012/07/19/latency-the-new-web-performance-bottleneck/)</figcaption>
</figure>
<blockquote>
<p>A lot of people think of bandwidth as the big speed bottleneck on the web.
But in 2012, Ilya Grigorik wrote that it turns out that the web’s performance problems have more to do with latency than bandwidth. What Ilya’s referring to is the physical distance our code needs to travel between the server and the browser. A trip to the server and back takes measurable time. Sometimes a lot of time.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/50.jpg" srcset="/images/delivering-responsibly/400/50.jpg 400w, /images/delivering-responsibly/800/50.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>50: Illustrated world map with requests transferring on top</figcaption>
</figure>
<blockquote>
<p>Our code is hurtling around the world over fiber wire. And even at nearly the speed of light, that distance takes time.</p>
</blockquote>
<blockquote>
<p>This is why CDNs matter. After optimizing size, you still have to factor in proximity delays. Round trips to the server can be hundreds of milliseconds at best, but often over a second.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/51.jpg" srcset="/images/delivering-responsibly/400/51.jpg 400w, /images/delivering-responsibly/800/51.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>51: Rendering Blockers: stylesheets and scripts</figcaption>
</figure>
<blockquote>
<p>What’s worse is that we often require many round trips just to start rendering a page. That’s because when a web page is requested, the first thing that is sent back to the browser is the HTML. If that HTML references external files that are required for rendering the layout, then the browser goes back to the server and fetches them, and we’ve made several round trips.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/52.jpg" srcset="/images/delivering-responsibly/400/52.jpg 400w, /images/delivering-responsibly/800/52.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>52: Man carrying all the grocieries [youtube](https://www.youtube.com/watch?v=mU03Yoby0Ss)</figcaption>
</figure>
<blockquote>
<p>So we want to minimize the round trips that occur in our critical rendering path. One way to avoid making many round trips is to carry more with you on each trip. Sort of like carrying groceries from the car. Anyone else done this before?</p>
</blockquote>
<blockquote>
<p>For a long time now, this is what we’ve had to do to make a website render fast: bring the important stuff back in the first trip from the server, get the rest later.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/53.jpg" srcset="/images/delivering-responsibly/400/53.jpg 400w, /images/delivering-responsibly/800/53.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>53: [Google Pagespeed Insights](https://developers.google.com/speed/pagespeed/insights/)</figcaption>
</figure>
<blockquote>
<p>Now, to do that in a way that works with any browser, we need a small workaround. Google has this tool called PageSpeed Insights - it’s full of great tips.</p>
</blockquote>
<blockquote>
<p>This is one tip related to this workaround: “identify and inline the CSS necessary for rendering above the fold content. ” The idea is that we need to avoid making additional trips for anything we’ll need to render the top portion of the page… <em>above the fold</em>, so to speak.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/54.jpg" srcset="/images/delivering-responsibly/400/54.jpg 400w, /images/delivering-responsibly/800/54.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>54: “The fold” varies…</figcaption>
</figure>
<blockquote>
<p>Now, I know what you might be thinking. “The fold!” And on the web there is no one fold. It varies across viewport sizes. There’s no one consistent viewport height across browsers and devices.</p>
</blockquote>
<blockquote>
<p>But this doesn’t need to be controversial advice. Pages are viewed from top to bottom. Google’s telling us to prioritize the code necessary to render the top of the page before the rest of the page. Cool? Cool.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/55.jpg" srcset="/images/delivering-responsibly/400/55.jpg 400w, /images/delivering-responsibly/800/55.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>55: Full CSS vs. Critical CSS</figcaption>
</figure>
<blockquote>
<p>In CSS, that prioritized code might look something like this.</p>
</blockquote>
<blockquote>
<p>If you can imagine the CSS on the left reflecting a site’s full stylesheet, then on the right is a subset of that CSS that is deemed critical to a particular page on the site, necessary for styling the top portion of the page across all of its layout breakpoints. So the “critical” CSS is a subset of the full CSS</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/56.jpg" srcset="/images/delivering-responsibly/400/56.jpg 400w, /images/delivering-responsibly/800/56.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>56: Article template from FilamentGroup.com, styled fully and partially</figcaption>
</figure>
<blockquote>
<p>Visually, it looks like this. On the left is an article page on Filament Group’s site. On the right is that same article with only that page’s critical CSS styles applied. The red line designates the line above which we consider to be the page’s critical rendering region, which in this case is around 1200x900px–a fairly generous size to cover most viewport heights and layout breakpoints. And you can see in the right graphic that below the red line, the design falls away, so we know we’ve isolated the subset of CSS for styling the top region.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/57.jpg" srcset="/images/delivering-responsibly/400/57.jpg 400w, /images/delivering-responsibly/800/57.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>57: [Grunt CriticalCSS tool](https://github.com/filamentgroup/grunt-criticalcss)</figcaption>
</figure>
<blockquote>
<p>So how do we know which portion of our CSS styles are for the top of the page? Easy! Just open up your CSS file and copy the first 100 lines or so! That’s it.</p>
</blockquote>
<blockquote>
<p>I’m kidding. We have great tools that analyze a site’s stylesheet compared to each particular template on a site, to extract the portion of styles that are used in rendering the critical portion of the page. One of those is a tool called Critical CSS, and Grunt CriticalCSS, which is a command line utility that you can run to perform tasks that you commonly need in preparing for delivery.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/58.jpg" srcset="/images/delivering-responsibly/400/58.jpg 400w, /images/delivering-responsibly/800/58.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>58: Critical CSS configuration</figcaption>
</figure>
<blockquote>
<p>At Filament we run our critical CSS task on each unique template of a particular site and write each subset of styles to a separate file. As you can see in this configuration file for our critical CSS tool on a sample project, there’s a <code>criticalcss</code> task with subtasks for each unique template on a site. Each subtask references a representative live URL that uses a particular template, as well as the filename of the site’s full CSS file, and a file name to write the critical CSS styles to for that template.</p>
</blockquote>
<blockquote>
<p>Every time we make changes to our code, that task runs and we get a new batch of critical css files for the site.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/59.jpg" srcset="/images/delivering-responsibly/400/59.jpg 400w, /images/delivering-responsibly/800/59.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>59: Inlining the critical CSS</figcaption>
</figure>
<blockquote>
<p>So that tool gives us a critical css file for each unique template. To avoid round trips, we are going to include the critical CSS right into the HTML, instead of referencing the CSS externally. This allows us to cram the important rules into that first round trip and render immediately upon arrival.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/60.jpg" srcset="/images/delivering-responsibly/400/60.jpg 400w, /images/delivering-responsibly/800/60.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>60: Inlining the critical CSS (cont.)</figcaption>
</figure>
<blockquote>
<p>To include CSS inline in the page we have the <code>style</code> element. Like this, right inside the head.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/61.jpg" srcset="/images/delivering-responsibly/400/61.jpg 400w, /images/delivering-responsibly/800/61.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>61: Inlining the critical CSS (cont.)</figcaption>
</figure>
<blockquote>
<p>In a real project, you’d likely set this up to include each critical CSS file dynamically, either through a server side include or a build process. So that might look like this. Again, that’s including ONLY the critical CSS, not all of the CSS.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/62.jpg" srcset="/images/delivering-responsibly/400/62.jpg 400w, /images/delivering-responsibly/800/62.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>62: Loading the rest of the CSS</figcaption>
</figure>
<blockquote>
<p>But we’ll also need to load our full stylesheet too, and without blocking rendering.</p>
</blockquote>
<blockquote>
<p>A regular old stylesheet link is not an option for this. Those stop rendering immediately, no matter where you put them in the DOM (at least in many browsers like Chrome, Webkits). A future-looking way we can now do this is to use a new standard called <code>link[rel=preload]</code>. It’s not supported in any browsers yet but if we’re lucky maybe <a href="https://jakearchibald.com/">Jake</a> will make it work in Chrome before I finish this talk.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/63.jpg" srcset="/images/delivering-responsibly/400/63.jpg 400w, /images/delivering-responsibly/800/63.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>63: Applying the full CSS</figcaption>
</figure>
<blockquote>
<p>Now, <code>rel=preload</code> will tell the browser to fetch that stylesheet, but it won’t <em>apply the stylesheet</em> once it loads. For that, we need to listen for its <code>onload</code> event and set the <code>link</code>’s <code>rel</code> attribute to “stylesheet” instead of “preload”.</p>
</blockquote>
<blockquote>
<p>So in any browser that supports <code>rel=preload</code>, this snippet alone will asynchronously fetch and apply a CSS file. But again, zero browsers support <code>rel=preload</code> right now, so that request will not be made. But it’s a nice way to reference our full stylesheet at least… maybe we could polyfill it for now.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/64.jpg" srcset="/images/delivering-responsibly/400/64.jpg 400w, /images/delivering-responsibly/800/64.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>64: Next, the critical JavaScript</figcaption>
</figure>
<blockquote>
<p>And polyfills tend to use JavaScript! So let’s jump over to JavaScript and inline the critical portion of that as well.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/65.jpg" srcset="/images/delivering-responsibly/400/65.jpg 400w, /images/delivering-responsibly/800/65.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>65: Which JavaScript is “critical”?</figcaption>
</figure>
<blockquote>
<p>Which brings me to the question, “what kind of JavaScript would be considered critical anyway?”</p>
</blockquote>
<blockquote>
<p>Well, ideally none of it. But having a little JavaScript running right away can help us deliver appropriately to newer browsers. Consider it “high-priority” JavaScript if you want–file loaders, feature tests, polyfills, and some logic to use those scripts to bootstrap your page. That sort of JS might belong in the <code>head</code> of your page.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/66.jpg" srcset="/images/delivering-responsibly/400/66.jpg 400w, /images/delivering-responsibly/800/66.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>66: Critical JS, inlined</figcaption>
</figure>
<blockquote>
<p>We don’t want to reference that JavaScript externally though. Like our critical CSS, we’ll want it to run immediately when the HTML is parsed, without delay. So we’ll inline it.</p>
</blockquote>
<blockquote>
<p>For Javascript inlining, we have the script element. We’ll use that to inline our critical JavaScript as well.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/67.jpg" srcset="/images/delivering-responsibly/400/67.jpg 400w, /images/delivering-responsibly/800/67.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>67: [loadCSS.js: an async CSS loader ](https://github.com/filamentgroup/loadCSS/)</figcaption>
</figure>
<blockquote>
<p>And for this example, my critical JavaScript will need to contain some file loading functions to load additional assets that aren’t as critical. One of those is a CSS loading function, which we can use to load our full CSS in a way that doesn’t block page rendering.</p>
</blockquote>
<blockquote>
<p>It’s called loadCSS. We can call that function and pass it a stylesheet url, and it’ll load and apply the stylesheet without blocking rendering, much like <code>rel=preload</code> will work, but in a way that works in most any browser today.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/68.jpg" srcset="/images/delivering-responsibly/400/68.jpg 400w, /images/delivering-responsibly/800/68.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>68: Detecting preload support</figcaption>
</figure>
<blockquote>
<p>Now that we have a CSS loading function, we can use it to polyfill that <code>link[rel=preload]</code> reference when necessary, to request our full stylesheet in any browser that doesn’t support <code>rel=preload</code>.</p>
</blockquote>
<blockquote>
<p>To know whether a browser supports preload, we’ll need a feature test. This is one that Simon Peiters came up with in the w3c threads. The test will fail in any browser today because the feature is not yet supported. But the idea is that it’ll begin to pass once support is adopted, and our polyfill will deprecate itself automatically.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/69.jpg" srcset="/images/delivering-responsibly/400/69.jpg 400w, /images/delivering-responsibly/800/69.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>69: Polyfilling rel=preload with loadCSS.js</figcaption>
</figure>
<blockquote>
<p>So we have those two functions, and now we can use them to polyfill that <code>rel=preload</code> tag.</p>
</blockquote>
<blockquote>
<p>That ends up looking something like this. If <code>rel=preload</code> isn’t supported, we’ll find that <code>link</code> in the page and load the file it references using loadCSS. So that’s how we load our “non-critical”, full CSS - the site’s full stylesheet.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/70.jpg" srcset="/images/delivering-responsibly/400/70.jpg 400w, /images/delivering-responsibly/800/70.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>70: [loadJS.js: an async script loader](https://github.com/filamentgroup/loadJS/)</figcaption>
</figure>
<blockquote>
<p>Moving on from CSS loading. I also typically include a JavaScript file loader, because I often have a lot more JavaScript to load than I want to inline in the head of the page. For example, a DOM framework and user interface improvements would be the sort of scripts to load later rather than inlining them in the head.</p>
</blockquote>
<blockquote>
<p>loadJS is a script we can use to do that, just like that loadCSS function we already used. Here, I’ve referenced my scripts as if they’re all packed into one file, “enhancements.js” for a single http request.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/71.jpg" srcset="/images/delivering-responsibly/400/71.jpg 400w, /images/delivering-responsibly/800/71.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>71: Async/defer is a nice alternative…</figcaption>
</figure>
<blockquote>
<p>Of course, if you just want to <em>load</em> any old JavaScript file without blocking render, the standard async and defer attributes make that really easy too. And they’re supported most everywhere.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/72.jpg" srcset="/images/delivering-responsibly/400/72.jpg 400w, /images/delivering-responsibly/800/72.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>72: But dynamic loaders let us qualify.</figcaption>
</figure>
<blockquote>
<p>But! What’s nice about loading scripts dynamically is we can decide whether or not to load the file AT ALL, in real time. Here’s a basic example that does just that, using an approach known as Cutting the Mustard to check if a couple of baseline features are supported before requesting the script. We can use this technique to decide whether to load our JS at all based on features that we know our enhancement scripting will need in order to work.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/73.jpg" srcset="/images/delivering-responsibly/400/73.jpg 400w, /images/delivering-responsibly/800/73.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>73: The full head of the page</figcaption>
</figure>
<blockquote>
<p>So to bring all that together, let’s step back and look at the <code>head</code> of our page and our CSS and JavaScript loading approach. Here it is. Up top we reference our critical and full css, and after that, we polyfill the CSS loading if needed and load our full JavaScript manually, if qualified. Also at the end, a <code>noscript</code> tag wraps a regular <code>link</code> to our full stylesheet, just to make it a little more bulletproof in JS-disabled environments.</p>
</blockquote>
<blockquote>
<p>For what it’s worth, we also tend to set a cookie whenever a file has been loaded, and we use the presense of that cookie to decide whether to link to that file directly on subsequent page visits, which is nice because we only need to include inline CSS when the full CSS is not yet in cache.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/74.jpg" srcset="/images/delivering-responsibly/400/74.jpg 400w, /images/delivering-responsibly/800/74.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>74: How does this change in http/2?</figcaption>
</figure>
<blockquote>
<p>So that process I just showed was dense, but I covered most of the considerations we think about for optimizing our critical rendering path.</p>
</blockquote>
<blockquote>
<p>Looking ahead, some of those steps may evolve a little bit with http/2 support in play. That’s because of one particularly neat feature called server push.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/75.jpg" srcset="/images/delivering-responsibly/400/75.jpg 400w, /images/delivering-responsibly/800/75.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>75: Server Push</figcaption>
</figure>
<blockquote>
<p>In http/2, network latency is still a problem, even though it makes better use of connections once you have them. Round trips to the server take time regardless of the protocol, which is why we inline things in http/1. Well, in http/2, inlining is not necessary anymore because of server push.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/76.jpg" srcset="/images/delivering-responsibly/400/76.jpg 400w, /images/delivering-responsibly/800/76.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>76: “index.html please?” ... “Sure! And here’s critical.css too.”</figcaption>
</figure>
<blockquote>
<p>Server push lets us decide to push more than one asset back to the browser when we know it’ll need them to render a particular page. So… a request for a particular HTML page might return that page, and also critical.css, full.css, critical.js, and whatever else is relevant to rendering that page fast. All in the same request &amp; connection!</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/77.jpg" srcset="/images/delivering-responsibly/400/77.jpg 400w, /images/delivering-responsibly/800/77.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>77: “if you have ever inlined a resource (CSS, JS, or an image), you've been "simulating" server push” - [Ilya Grigorik](https://www.igvita.com/2013/06/12/innovating-with-http-2.0-server-push/)</figcaption>
</figure>
<blockquote>
<p>What currently takes many round trips in http/1 can be done in 1 trip in http/2. So this feature negates the need for inlining CSS in our HTML. It negates the need for inlining anything, really. Well, for http/2-supporting browsers that is.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/78.jpg" srcset="/images/delivering-responsibly/400/78.jpg 400w, /images/delivering-responsibly/800/78.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>78: PHP example: link and push, or inline?</figcaption>
</figure>
<blockquote>
<p>So what that might mean is, we will still want to extract the critical CSS for each template and drop it into a file (because we still want to request the smallest amount of CSS necessary up-front to render the page), but we won’t want to <em>inline</em> that file anymore for http/2-supporting browsers, since we can reference it with a normal stylesheet link and use server push to return it with the HTML.</p>
</blockquote>
<blockquote>
<p>And that’s a nice improvement because then the file is cached for future visits to templates that share the same critical CSS file.</p>
</blockquote>
<blockquote>
<p>But again, we will still want the benefits of faking server push in http/1 browsers, so some server negotiation will likely be best here: inline the css for http/1 browsers, or preload/push it in http/2.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/79.jpg" srcset="/images/delivering-responsibly/400/79.jpg 400w, /images/delivering-responsibly/800/79.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>79: Also: Stylesheets in the body…?!</figcaption>
</figure>
<blockquote>
<p>And on the topic of requesting that full CSS file, there’s another feature coming that’ll be an interesting alternative to consider for loading our full CSS. The Chrome team is considering changing how it requests stylesheets linked from the <code>body</code> of the page so that it will only block rendering of content that comes after it that stylesheet <code>link</code>.</p>
</blockquote>
<blockquote>
<p>Currently in Chrome (and webkit too), a stylesheet <code>link</code> anywhere in the dom will block all rendering immediately when its parsed–before anything is shown to the user. Soon though, this could allow a full.css or even a secondary.css to be linked just after the “fold” portion of your template (which would be a specific spot based on an assumed height). Jake Archibald has been pushing this approach and I think it’s really nice, particularly because it lets us request non-critical css with markup alone. It might even turn out that this feature would allow us to make many component-based CSS requests throughout the page, just before the markup that utilizes that CSS.</p>
</blockquote>
<blockquote>
<p>Chrome’s change would bring it in line with the behavior of several other browsers as well, so we could probably use it right away. The jury’s out on whether iOS and Safari will follow their lead, however, as they currently block all rendering for any stylesheet placement, just like Chrome. So this is a feature to keep on the radar for now.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/80.jpg" srcset="/images/delivering-responsibly/400/80.jpg 400w, /images/delivering-responsibly/800/80.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>80: Font Loading</figcaption>
</figure>
<blockquote>
<p>So those are some considerations for moving CSS and JavaScript to non-blocking requests so we can show a page as soon as it arrives.</p>
</blockquote>
<blockquote>
<p>Yet! Despite having this workflow in place, a major blocker to perceived performance still comes from custom fonts. That’s because the default way many browsers load custom fonts right now is kind of awful. Many browsers, such as iOS Safari, hide the text of the page entirely while a custom font is loading - a behavior we often call the flash of invisible type.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/81.jpg" srcset="/images/delivering-responsibly/400/81.jpg 400w, /images/delivering-responsibly/800/81.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>81: The FOIT</figcaption>
</figure>
<blockquote>
<p>Browsers like iOS Safari will hide the page content while custom fonts are loading for 30 seconds or more. For example, this is the Fast Co site when it loads. if you’re an iOS user this probably looks familiar because it happens a lot.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/82.jpg" srcset="/images/delivering-responsibly/400/82.jpg 400w, /images/delivering-responsibly/800/82.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
<figcaption>82: [Font Loading Revisited with Font Events](http://www.filamentgroup.com/lab/font-events.html)</figcaption>
</figure>
<blockquote>
<p>Recently we’ve been taking an approach to font loading that involves listening for when a font finishes loading, and then enabling it in our layout. To do that, we use a script called <a href="https://github.com/bramstein/fontfaceobserver">FontFaceObserver by Bram Stein</a>. And we use it to add a class to the <code>html</code> element when the fonts are all ready, and we use that class to enable them in CSS, otherwise allowing a fallback font to show.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/83.jpg" srcset="/images/delivering-responsibly/400/83.jpg 400w, /images/delivering-responsibly/800/83.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>83: Enabling fonts when loaded</figcaption>
</figure>
<blockquote>
<p>In CSS, that qualification is similar to the pattern you might recognize from Modernizr. First we style an element with a default system font, then we enable the custom font in a qualified way.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/84.jpg" srcset="/images/delivering-responsibly/400/84.jpg 400w, /images/delivering-responsibly/800/84.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>84: ..continued</figcaption>
</figure>
<blockquote>
<p>Here’s how the JavaScript looks for applying that class to qualify the CSS.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/85.jpg" srcset="/images/delivering-responsibly/400/85.jpg 400w, /images/delivering-responsibly/800/85.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>85: Progressive Font Rendering</figcaption>
</figure>
<blockquote>
<p>In this timeline of Filament Group’s homepage, you can see what this approach does. The text is visible as soon as possible, while the custom font loads at its convenience. And this works today in any browser.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/86.jpg" srcset="/images/delivering-responsibly/400/86.jpg 400w, /images/delivering-responsibly/800/86.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>86: Coming soon: [CSS font-display](https://tabatkins.github.io/specs/css-font-display/)</figcaption>
</figure>
<blockquote>
<p>To pull of this sort of progressive font rendering in the future, we won’t need JavaScript listeners at all. The new CSS <a href="https://tabatkins.github.io/specs/css-font-display/">font-display specification</a> covers several styles of prioritizing the way custom type renders. Here I’m showing the font-display property called “swap”, which will immediately render text in a fallback font and swap in the custom font when it loads. Other values allow you to hide text rendering for a duration, or even choose to only use a font if it’s already in cache from a previous visit (which may be preferable to most users, while hard to embrace as a designer).</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/87.jpg" srcset="/images/delivering-responsibly/400/87.jpg 400w, /images/delivering-responsibly/800/87.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>87: Case study: Wired usable 8s faster (3g)!</figcaption>
</figure>
<blockquote>
<p>So those are some techniques we can use to improve our delivery today and tomorrow. And if you’re interested, there’s <a href="https://www.filamentgroup.com/lab/weight-wait.html">an article on Filament’s site that shows just how much these practices can help</a>.</p>
</blockquote>
<blockquote>
<p>In the article I did a case study on that 11+ megabyte <a href="http://Wired.com">Wired.com</a> Article shown earlier. And just by optimizing the rendering path alone, without changing the weight of the page at all, I was able to cut almost 9 seconds off the time it takes for that page to be usable on a 3G connection. From 12 seconds down to 4 or so. So it’s amazing how much impact these practices can have.</p>
</blockquote>
<figure class="emphasize">
<img src="/images/delivering-responsibly/400/88.jpg" srcset="/images/delivering-responsibly/400/88.jpg 400w, /images/delivering-responsibly/800/88.jpg 690w" sizes="(min-width: 690px) 690px, 100vw" alt="">
<figcaption>88: Building for resilience is our job.</figcaption>
</figure>
<blockquote>
<p>So, I know I’ve covered a lot in this talk. Indeed, I think we have a lot to think about.</p>
</blockquote>
<blockquote>
<p>But to close things out, I think we really need to remember that we can build beautiful, complex things that are broadly accessible. We have the tools today.</p>
</blockquote>
<blockquote>
<p>In truth, it’s often hard work to deliver responsibly–and resiliently–but it’s also our job.</p>
</blockquote>
<figure>
<img src="/images/delivering-responsibly/400/89.jpg" srcset="/images/delivering-responsibly/400/89.jpg 400w, /images/delivering-responsibly/800/89.jpg 690w" sizes="(min-width: 690px) 288px, 100vw" alt="">
</figure>
<blockquote>
<p>Thanks everyone!</p>
</blockquote>
Filament Group Wins Agency of the Year2015-09-21T00:00:00-04:00<h1>Filament Group Wins Agency of the Year at 2015 Net Awards</h1>
<p>Posted by FG on 09/21/2015</p>
<div class="figure-b"><a href="https://thenetawards.com/"><img src="/images/netawards-blog.png" alt="Net Awards 2015 logo"></a></div>
We’re thrilled to announce that Filament Group was voted <a href="https://thenetawards.com/">Agency of the Year at the 2015 Net Awards</a> in London last week.
The Net Awards, hosted by [Net Magazine](http://www.creativebloq.com/net-magazine), are one of the most widely-recognized awards shows in the web industry, and all of its categories are voted and adjudicated by our peers. In addition to winning Agency of the Year, our own Scott Jehl was shortlisted for [Developer of the Year](https://thenetawards.com/vote/developer/) as well.
<p>Our work is inspired by countless others in our community, and we’re very thankful to be recognized by these peers.
We believe that practices such as progressive enhancement and responsive design are critical to building resilient and broadly-accessible web services today. Perhaps this award goes to show just how many like-minded folks there are out there…</p>
<p>Thank You!</p>
More Weight Doesn't Mean More Wait2015-04-17T00:00:00-04:00<h1>More Weight Doesn’t Mean More Wait</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> on 04/17/2015</p>
<p>When I talk about web performance, I like to use the phrase, “weight does not need to increase wait.” To be clear, that’s <em>not</em> because the weight of a webpage doesn’t matter—it most definitely does—but rather because we can often (<em>usually</em>, even) deliver a usable representation of a web page’s content very quickly, even if that page is quite large and asset-heavy as a whole. At the root of this distinction is a performance metric that the web community has only recently begun to discuss and prioritize, known as <em>perceived performance</em>.</p>
<p>Formerly, much of the focus in web performance was concerned with optimizing assets like images and fonts, which does make for a shorter overall page load time. But today there are techniques we can use in addition to file optimization that have an arguably larger impact on how soon our users can see and use the content we’re delivering.</p>
<h2 id="measuring-overall-%26-perceived-performance">Measuring Overall &amp; Perceived Performance <a class="direct-link" href="#measuring-overall-%26-perceived-performance" aria-hidden="true">#</a></h2>
<p>Part of the reason we hadn’t focused heavily on perceived performance in the past was due to a lack of good tools to analyze the events that take place while our pages load. Nowadays, that’s hardly the case.</p>
<p>Amongst a few others, <a href="http://www.webpagetest.org/">WebPageTest.org</a> sits at the top of the list of tools I find irreplaceable in profiling performance today. In short, WPT’s web interface allows you to test a site in a variety of browser/device/location combinations and receive loads of rich information about how that site loaded and what you can do to improve it.</p>
<p>Here’s a glance at the summary table you see when you run WPT on a site, in this case, I tested <a href="http://www.wired.com/2015/03/our-new-site/">an article page on the new responsive Wired.com</a> on a 3G connection from an Android device in Dulles, Virginia, USA using the Chrome browser:</p>
<p><img src="/images/weight-wait/wpt-summary.png" alt=""></p>
<p class="note"><em><strong>A quick note:</strong> I should point out here that while this particular example website can use some performance improvements, this post is not meant to be a critique of Wired's site specifically. In truth, most sites of that scale perform similarly to this one, so it's just one of many examples we could use</em>.</p>
<p>This summary table provides some useful numbers, like:</p>
<ul>
<li>Overall page load time: the time it takes all of the page’s assets to finish loading</li>
<li>Overall page weight: the combined weight of the page’s HTML and all the files that it references (CSS, javascript, images, fonts, etc.)</li>
</ul>
<p>There’s even a new column telling you how much that page costs to load in various parts of the world (which ties into Tim Kadlec’s excellent site, <a href="http://whatdoesmysitecost.com/">WhatDoesMySiteCost.com</a>). This particular page weighs in at 8-9 megabytes including all of its assets and advertising (its weight seems to vary depending on the ads in play), which <a href="http://whatdoesmysitecost.com/test/150416_09_71875497c6009dd92716f8d9aa5f3af6">costs over $1 dollar (US) to access, even in developed regions of the world like Germany</a>!</p>
<p>The metrics mentioned above represent areas we’ve traditionally looked to when reviewing performance at a high-level. They’re areas that we can dramatically improve by optimizing our file sizes (images, fonts, JavaScript, CSS) and minimizing the number of third party sites we reference for ads and social widgets.</p>
<p>However, there are also values in the summary table that tell us a bit more about how fast a site <em>feels</em>, and how soon it can be used. Fortunately for us, a site can typically be used long before it has finished loading, so again, the weight doesn’t need to impact the wait!</p>
<p>For measuring perceived performance in WPT, I tend to look at these values:</p>
<ul>
<li>Time to first byte: the time it takes for the site to return the first byte of its response content</li>
<li>Start render: The time at which a site’s content starts to visually display in the browser</li>
</ul>
<p><img src="/images/weight-wait/wpt-summary-highlighted.png" alt=""></p>
<p>Also useful for this is WPT’s timeline view, which lets us see how the page progressively renders over the course of recorded keyframes. Here’s an abbreviated version of that page’s timeline, viewed at intervals of 500 milliseconds and cropped to show the important rendering steps:</p>
<p><img src="/images/weight-wait/wpt-timeline-live.png" alt=""></p>
<p>The timeline view is particularly handy because it gives a little more meaning to your initial rendering steps. In this case, the page starts rendering after about 10.5 seconds but there’s no content until about 14 seconds. That’s a long time to wait around on your phone!</p>
<h2 id="improving-perceived-performance">Improving Perceived Performance <a class="direct-link" href="#improving-perceived-performance" aria-hidden="true">#</a></h2>
<p>In viewing the time to first byte, we get some insight into the time the web server takes to process a request from a given location and connection speed, and perhaps how much network latency is factoring into the round trip to the server and back (which is, <a href="https://www.igvita.com/2012/07/19/latency-the-new-web-performance-bottleneck/">as Ilya Grigorik kindly points out</a>, the web’s current performance bottleneck).</p>
<p>First byte time can be reduced in a number of ways (none of which are likely to be easy), such as shortening the time a server spends handling a request, <a href="https://istlsfastyet.com/">updating your SSL configration</a>, and delivering static HTML responses when possible. Maybe most importantly, distributing your code throughout the world using a CDN (like <a href="https://www.cloudflare.com/">Cloudflare</a>, <a href="http://www.fastly.com/">Fastly</a>, etc.) will have a huge impact on time to first byte because the physical distance between a user and the remote web server matters a great deal. WPT’s waterfall view gives us some information about how much time is spent in DNS lookup and while negotiating SSL so you can see how much of that time to first byte was spent in each phase of the round trip.</p>
<p><img src="/images/weight-wait/wpt-waterfall.png" alt=""></p>
<p>As for reducing start render time, the bottlenecks are often on the client-side, in the way we write our HTML. That’s because the ways in which an HTML file references external CSS and JavaScript files frequently causes the browser to stop visually rendering the page until it finishes loading and parsing those files.</p>
<p>To figure out which files are referenced in render-blocking ways, we can look at that waterfall chart again. The green vertical line in the image represents the point when the page starts to render. We can see requests to the left of that line that are potentially causing that rendering to happen later. These are files we’ll want to focus on first, since they may be blocking rendering.</p>
<p>A better tool for identifying render-blocking resources is <a href="https://developers.google.com/speed/pagespeed/insights/">Google’s PageSpeed Insights</a>. Just like WPT, you can submit a URL and PSI will give you a performance score (1-100), and tips for improving that performance. Below is a screenshot of <a href="https://developers.google.com/speed/pagespeed/insights/?url=www.wired.com%2F2015%2F03%2Four-new-site%2F">PSI’s advice for this particular page</a> to “Eliminate render-blocking JavaScript and CSS.” PSI found 3 blocking CSS files and 3 blocking JavaScript files, all of which present a potential single point of failure for rendering the page in a timely manner.</p>
<p><img src="/images/weight-wait/pagespeed.png" alt=""></p>
<p>Anything we can do to request those files in a non-blocking manner will help the browser to start rendering the page sooner.</p>
<h2 id="improving-start-render">Improving Start Render <a class="direct-link" href="#improving-start-render" aria-hidden="true">#</a></h2>
<p>A page with no external JavaScript or CSS references can start rendering as soon as it arrives, and so can one that references external JavaScript and CSS in a non-blocking manner. From a technical standpoint, that means requesting files asynchronously, or… not requesting them externally at all!</p>
<p>With regards to JavaScript, this is where we start to consider things like moving our JavaScript references to the end of the document, adding <code>async</code> and <code>defer</code> attributes to our <code>script</code> tags, or using <a href="https://github.com/filamentgroup/loadJS">a small bit of inline JavaScript</a> to request them dynamically (which is useful if you want to qualify an asynchronous request based on browser features, by <a href="http://responsivenews.co.uk/post/18948466399/cutting-the-mustard">Cutting the Mustard</a>).</p>
<p>With regards to CSS, this is where we start to consider things like inlining the most important, or “critical” portion of our CSS rules in the <code>head</code> of the page and requesting our site’s full stylesheet asynchronously. The latter is more difficult than it should be. That’s because unfortunately, it doesn’t matter where you place a <code>link</code> in your document… unlike JavaScript, a stylesheet reference is going to halt rendering as soon as it takes flight in most browsers. (<a href="https://github.com/filamentgroup/loadCSS">This little bit of JavaScript</a> in the <code>head</code> of your page is the best we can do for async CSS loading for now, but hopefully the standards will improve with <code>link</code> element attribute additions soon enough).</p>
<p>This process of moving our JavaScript and CSS to a non-render-blocking delivery is detailed in our post <a href="http://www.filamentgroup.com/lab/performance-rwd.html">How we make RWD sites load fast as heck</a>. Go there to read more about it, and be sure to check out <a href="https://adactio.com/journal/8504">Jeremy Keith’s followup of how we implemented our recommendations</a> for one of his own sites.</p>
<h3 id="let%E2%80%99s-not-forget-the-fonts%E2%80%A6">Let’s not forget the fonts… <a class="direct-link" href="#let%E2%80%99s-not-forget-the-fonts%E2%80%A6" aria-hidden="true">#</a></h3>
<p>There’s one more thing we think about with regards to improving start render: custom font loading. Unfortunately, when custom fonts are in play much of our start render tuning can be for naught, particularly in browsers like iOS Safari. That’s because most browsers will hide the text in the page that uses a custom font until that font has finished loading, meaning the initial rendering of the page is pretty much useless, no matter how fast it draws.</p>
<p>To fight this behavior (and make users happy!), we recommend employing a progressive approach to rendering fonts so that text renders immediately in a fallback font while custom fonts are still being fetched. When doing this, it really helps to style your fallback fonts to match as closely as possible in size and spacing to the custom ones so the reflow between the two is minimized.</p>
<p>Here’s a recent post that explains how we do that here at FG: <a href="http://www.filamentgroup.com/lab/font-events.html">Font Loading Revisited with Font Events</a>. And while you’re thinking about font loading, check out Ilya Grigorik’s latest post, “<a href="https://www.igvita.com/2015/04/10/fixing-the-blank-text-problem/">Fixing the Blank Text Problem</a>,” which makes the case for an upcoming CSS property that will help us manage custom font loading with much less work.</p>
<h2 id="back-to-weight-vs.-wait!">Back to Weight vs. Wait! <a class="direct-link" href="#back-to-weight-vs.-wait!" aria-hidden="true">#</a></h2>
<p>To illustrate just how much these perceived performance optimizations can help, I applied them to a copy of that Wired article page analyzed above. To compare the before and after results fairly, I ran tests on the rendering time of an unchanged copy of the page and an optimized copy of the page, both hosted on one of our servers. The copied version of the page references the same external third-party assets and has almost identical performance timing to Wired’s real page, but it’s important to note that these optimizations are just a proof of concept and not applied to the actual Wired site. Actual results would vary a bit, but we can still get an idea of how much they’d help.</p>
<p>First, here’s a WPT timeline view the unedited copy of that page on our servers, tested on 3G connection in Android Chrome. I’ve cropped it to show the relevant portion of the timeline where the page starts to become usable (click the image to see it in full if you’d like):</p>
<p><a href="/images/weight-wait/wpt-copy-unedited.png"><img src="/images/weight-wait/wpt-copy-unedited.png" alt=""></a></p>
<p class="caption"><small>A timeline of the copied version of the live web page, with a first usable render at about 12.5 seconds.</small></p>
<p>Okay good, the render timing is fairly similar. This copy of the page is slightly faster, probably because I’m serving a static copy of the HTML and it seems to have a faster turnaround time on our servers, among other things. That said, it’s close enough for our purposes.</p>
<p>Next I applied some optimizations to that page, like:</p>
<ul>
<li>Extract and Inline “Critical” CSS</li>
<li>Load full StyleSheets asynchronously (cached for return visits)</li>
<li>Load all JavaScript asynchronously (yes, ads too!)</li>
<li>Load fonts asynchronously and apply them in a progressive manner (fallback first)</li>
<li>Style fallback fonts to match custom font sizes</li>
</ul>
<p>And here’s how it turns out. Here’s the optimized version tested in WPT under the same conditions as above (3G connection, Android Chrome), once again cropped to show the most relevant portion of the timeline:</p>
<p><a href="/images/weight-wait/wpt-copy-after.png"><img src="/images/weight-wait/wpt-copy-after.png" alt=""></a></p>
<p class="caption"><small>A timeline of the copied version of the live web page, with a first usable render at about 3.9 seconds.</small></p>
<p>That’s a full 8.5 seconds faster! Here’s a comparison view:</p>
<p><a href="/images/weight-wait/wpt-copy-compare.png"><img src="/images/weight-wait/wpt-copy-compare.png" alt=""></a></p>
<p class="caption"><small>Before and after, compared in one image.</small></p>
<p>Interestingly, the page ends up rendering to its intentional design in a about the same amount of time in both cases, but the optimized version is usable much much sooner.</p>
<p>Also, even after making these optimizations, both versions of the page still weighed about the same (8-9 megabytes). So just as a reminder, I did not optimize the size of any assets to get that improvement. But if I did, it would only help to make that page perform even better.</p>
<p><strong>More weight doesn’t mean more wait</strong>!</p>
<h2 id="looking-ahead%E2%80%A6">Looking ahead… <a class="direct-link" href="#looking-ahead%E2%80%A6" aria-hidden="true">#</a></h2>
<p>So these are some things we can do right now to improve the perceived performance of any browser that accesses our site. But workarounds are workarounds… it would be great if we didn’t need to do them. Thankfully, there are some standards-based changes afoot on the web that will eventually render these techniques unnecessary in browsers of today and the future.</p>
<p>One of those is the latest version of HTTP (version 2), which browsers and web servers are just beginning to support (some sites already use it today). HTTP2 <a href="https://www.igvita.com/2013/06/12/innovating-with-http-2.0-server-push/">brings features like Server Push</a>, which will allow a webserver to respond to a request for an HTML page with not only that page’s HTML content, but also the files that it will need to render the page (such as CSS, JavaScript, fonts, and images). This amazing improvement will mean browsers can make much better use of that round trip to the server and back, and render pages very quickly without the need for inlining CSS and JavaScript. Other workarounds we’ve been using for years like image sprites, domain sharding, and concatenating files won’t be needed on HTTP2 either due to its dramatic improvements in streaming requests. Interesting stuff.</p>
<p>Of course, the switch to HTTP2 won’t happen overnight, as both servers and browsers need to speak the new protocol. Also, for many browsers the switch will never happen at all! For example, anyone who browses the web with Internet Explorer versions older than 11, Android 2, BlackBerry, and others, won’t be able to communicate in HTTP2 with sites. That means we’ll have to keep both protocols alive on our sites for a long time, and come up with strategies for taking advantage of HTTP2 features without hampering the experience for browsers that don’t understand it.</p>
<p>That kind of sounds like… progressive enhancement! Time will tell how these approaches will pan out alongside one another, and you can <a href="https://www.mnot.net/blog/2014/01/30/http2_expectations">read more about what HTTP2 will bring here</a>.</p>
<p>Thanks for reading!</p>
How We Load Web Fonts Progressively2015-02-16T00:00:00-05:00<h1>How We Load Web Fonts Progressively</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> and <a href="/about/#zach-leatherman">Zach</a> on 02/16/2015 (updated 11/1/2016).</p>
<p><em>Note: This article’s title was updated for clarity. It was formerly called “Font Loading Revisited with Font Events.”</em></p>
<p>Last month <a href="/lab/font-loading.html">we wrote about an approach</a> we’d been using to load web fonts in a more responsible manner than browsers tend to do by default. The purpose of the approach was to avoid a typically undesirable browser behavior we often refer to as the “FOIT” (Flash of Invisible Text), in which a browser hides all text that should be styled with a custom font until that font has finished loading.</p>
<h2 id="a-brief-recap-on-the-foit">A brief recap on the FOIT <a class="direct-link" href="#a-brief-recap-on-the-foit" aria-hidden="true">#</a></h2>
<p>The <em>FOIT</em> tends to be most problematic in browsers like iOS Safari, which hides text for up to 30 seconds before giving up and rendering it with a default font, but it can also be seen in browsers with shorter hiding durations like Chrome, Firefox, and Opera as well. For example, here’s how our site would load in Chrome on a 3G-ish connection if we were loading fonts in a standard way through CSS <code>@font-face</code>. Note that the page content is available for rendering at around 1.7 seconds in the timeline, yet the text is hidden until fonts have finished loading.</p>
<p><img src="/images/font-loading-blog-post/default-thumb.png" alt="Timeline of our website using standard font loading. On a 3G connection."></p>
<p class="caption"><small>FIG 1: Timeline of our website using standard custom font loading. On a 3G connection.</small></p>
<p>It’s nice that these browsers limit their text hiding to 3 seconds, but even 3 seconds is a long time to wait for content that’s already downloaded. After all, the performance community tends to agree that 1 second is a reasonable goal for rendering a usable page on a fast connection, and we tend to shoot for rendering something useful in 2 seconds on slower devices on mobile networks as well. Since a page typically requires text to be usable, FOIT timeouts are a concern across most browsers, not just iOS Safari. But again, that browser’s behavior is <em>by far</em> the worst.</p>
<h2 id="a-workable-workaround">A workable workaround <a class="direct-link" href="#a-workable-workaround" aria-hidden="true">#</a></h2>
<p>Our initial approach to working around the FOIT involved converting font files into Data URIs so that those fonts could be embedded directly into font-face definitions in a CSS file. By loading that CSS file in an asynchronous manner (using <a href="https://github.com/filamentgroup/loadCSS">a bit of JavaScript</a>), we could ensure that a browser would immediately render the text in the page using fallback fonts, and the custom fonts would apply once the CSS finished loading.</p>
<h3 id="an-unexpected-side-effect">An unexpected side effect <a class="direct-link" href="#an-unexpected-side-effect" aria-hidden="true">#</a></h3>
<p>That approach seemed to serve us well, but we recently started noticing that sites using this particular approach sometimes exhibited a small side effect: a brief FOIT or blink that happens long <em>after</em> the fonts finish loading, just before they are applied to the page. It didn’t seem to happen all the time, but we’d been seeing it crop up more and more regularly. The page loading timeline below of our site accessed over a fast connection while using this approach displays this peculiar blip.</p>
<p><img src="/images/font-loading-2/datauri-blip.png" alt="Timeline of our website using data uri font loading"></p>
<p class="caption"><small>FIG 2: Timeline of our website using async-loaded data URI fonts.</small></p>
<p>In this timeline, the page is usable (with fallback fonts) at around 600ms, but then for about 100ms, a FOIT occurs before the page ultimately returns to a usable state at 800ms. Looking at the request timing, we could see that the FOIT began just after the fonts finished loading. Of course, 800ms is a very fast page load, and a 100ms blink may not be the end of the world, but we found that the problem tended to display more prominently on slower connections and devices and on other sites as well. For example, here’s the same load on 3G:</p>
<p><img src="/images/font-loading-2/datauri-3G-blip.png" alt="Timeline of our website using data uri font loading. 3G connection."></p>
<p class="caption"><small>FIG 3: Timeline of our website using async-loaded data URI fonts. 3G connection.</small></p>
<p>Okay, okay. So another 100ms of hidden text still isn’t terrible, but it was certainly <em>odd</em>. And we sometimes saw longer delays of 500ms or so on client sites that were utilizing more custom fonts than our site does.</p>
<p>Something was up, and it seemed we had a little more work to do.</p>
<h2 id="finding-the-source-of-the-problem">Finding the source of the problem <a class="direct-link" href="#finding-the-source-of-the-problem" aria-hidden="true">#</a></h2>
<p>At first we wondered if the blink was just an artifact of repainting/reflowing a moderately complex layout. But the characteristic hidden text alongside visible graphic elements sure looked like your run-of-the-mill FOIT, and sure enough, it was. In short, a FOIT happens when a browser attempts to style an HTML element with a font-family that is defined (via a <code>@font-face</code> declaration) but not yet loaded. Interestingly enough, in this case it appeared that although the CSS and its included fonts had indeed already been delivered over the network to the browser, the browser still seemed to hide the text while parsing the data URI string, which we know can take a little time, particularly on slower devices.</p>
<h2 id="enabling-fonts-when-they%E2%80%99re-really-loaded">Enabling fonts when they’re really loaded <a class="direct-link" href="#enabling-fonts-when-they%E2%80%99re-really-loaded" aria-hidden="true">#</a></h2>
<p>After realizing that even data URIs can introduce a FOIT while they’re being parsed, we wanted to ensure that we applied our fonts to the page only after they were truly ready to render. Fortunately, Zach here had recently written <a href="https://dev.opera.com/articles/better-font-face/">a great article over at Dev.Opera about an upcoming font events API</a> that is designed for this specific purpose, and there are some nice lightweight polyfills available (<a href="https://github.com/zachleat/fontfaceonload">1</a>, <a href="https://github.com/bramstein/fontfaceobserver">2</a>) to start experimenting with it today. To experiment with a font events approach, we gave <a href="https://github.com/bramstein/fontfaceobserver">Bram Stein’s FontFaceObserver script</a> a try.</p>
<p>Here’s how it looks to use <code>fontfaceobserver</code> to set up a loading listener for one of the fonts used on this site (Open Sans Pro):</p>
<pre><code>new w.FontFaceObserver( &quot;Source Sans Pro&quot; )
.check()
.then( function(){ console.log( “Loaded!” ); });
</code></pre>
<p>You can read more about how to use Bram’s script on Github, but in short, you can specify a font family and other identifying details such as the font’s weight and style. Once the observer is created, you just need to <code>check()</code> it, and then get a callback when it finishes loading (which is easy to do through the <code>then()</code> method). Neat!</p>
<p>With this tool in hand, we followed a clever idea from Zach’s Opera article to qualify the use of our fonts throughout the site through a class selector, which we could add once the fonts were loaded. For example, the <code>body</code> element would reference a fallback <code>sans-serif</code> font until a class of <code>fonts-loaded</code> was present on the <code>html</code> element:</p>
<pre><code>body {
font-family: sans-serif;
}
.fonts-loaded body {
font-family: Source Sans Pro, sans-serif;
}
</code></pre>
<p>And our font observer callback can add that class when the font loads!</p>
<pre><code>new w.FontFaceObserver( &quot;Source Sans Pro&quot; )
.check()
.then( function(){
w.document.documentElement.className += &quot; fonts-loaded&quot;;
});
</code></pre>
<p>With that logic in place, our data URI fonts layered in without a blink. Great!</p>
<p><img src="/images/font-loading-2/datafontevents.png" alt="Timeline of our website using async-loaded data URI fonts with font events. No FOIT!"></p>
<p class="caption"><small>FIG 4: Timeline of our website using async-loaded data URI fonts with font events. No FOIT!</small></p>
<h2 id="great.-so-why-the-uris%3F">Great. So why the URIs? <a class="direct-link" href="#great.-so-why-the-uris%3F" aria-hidden="true">#</a></h2>
<p>At this point, we had a fix, but it got us thinking that maybe we didn’t need to go through the whole data URI route anymore, now that we were using a font events polyfill anyway. After all, these new font loading and listening tools are designed to help load fonts referenced via regular old CSS <code>@font-face</code> declarations, and who knows, maybe that’d be simpler and faster as well.</p>
<p>To make the switch, we removed the CSS file containing our data URIs, and the JavaScript logic we had used to load that CSS, and placed standard <code>@font face</code> rules referencing Woff2, Woff, and TTF files in our CSS, like this:</p>
<pre><code>@font-face {
font-family: 'Source Sans Pro';
src: url('/css/type/sourcesanspro-light-webfont.woff2') format('woff2'),
url('/css/type/sourcesanspro-light-webfont.woff') format('woff'),
url('/css/type/sourcesanspro-light-webfont.ttf') format('truetype');
font-weight: 300;
font-style: normal;
}
body {
font-family: sans-serif;
}
.fonts-loaded body {
font-family: Source Sans Pro, sans-serif;
}
</code></pre>
<p>Conveniently, the FontFaceObserver script will actually load a font for you when you call it, as long as the font you’re observing is referenced in a <code>@font-face</code> declaration in the CSS. It does this by generating an HTML element and styling it with the font you’re referencing, which causes the browser to kick off a request to its format of choice.</p>
<h2 id="the-result%3A-faster-than-ever!">The Result: Faster than ever! <a class="direct-link" href="#the-result%3A-faster-than-ever!" aria-hidden="true">#</a></h2>
<p>By referencing our fonts using CSS <code>@font-face</code> and using font loading APIs to load and enable them when ready, we’ve found our fastest page load yet (complete in 600 milliseconds on wifi!) while retaining the progressive font rendering we desired.</p>
<p>Here’s a timeline of our homepage now, loaded over a wifi connection.</p>
<p><img src="/images/font-loading-2/fontevents-final.png" alt="Timeline of our website using and font events. On a wifi connection."></p>
<p class="caption"><small>FIG 5: Timeline of our website using `@font-face` and font events. On a wifi connection.</small></p>
<h3 id="last-step%3A-optimizing-return-visits">Last step: Optimizing return visits <a class="direct-link" href="#last-step%3A-optimizing-return-visits" aria-hidden="true">#</a></h3>
<p>For return visits, we wanted to see if we could enable the font as soon as possible, knowing that it’d likely be cached in the browser from a prior load. To do this, we simply set a cookie after the fonts finish loading on the first visit. Next page view, the server side checks for that cookie and sets the <code>fonts-loaded</code> class in the HTML source that’s delivered to the browser. Using SSI for example, that looks like this:</p>
<pre><code>
&lt;!--#if expr=&quot;$HTTP_COOKIE=/fonts\-loaded\=true/&quot; --&gt;
&lt;html lang=&quot;en&quot; class=&quot;fonts-loaded&quot;&gt;
&lt;!--#else --&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;!--#endif --&gt;
</code></pre>
<p>And with that tweak, our return visits look as fast as can be. We have a complete render at 300ms on a fast connection:</p>
<p><img src="/images/font-loading-2/fontevents-repeat.png" alt="Timeline of our website's return visit timing on wifi."></p>
<p class="caption"><small>FIG 6: Timeline of our website's return visit timing on wifi.</small></p>
<p>But what’s particularly nice about this is that it is able to optimize <em>all</em> further browsing on the site, not just revisiting a page a user has already seen.</p>
<h2 id="looking-ahead">Looking ahead <a class="direct-link" href="#looking-ahead" aria-hidden="true">#</a></h2>
<p>By using font events and a clever polyfill, we were able to get our fonts to load progressively, saving our users from the dreaded FOIT. Looking ahead, we may be able to specify that our fonts load this way with a simple <code>font-rendering</code> CSS property instead of having to bother with JavaScript font APIs at all. For example, to specify that fonts should load progressively, as we’ve designed above, we can simply specify the following in our CSS: <code>font-rendering: swap;</code>. Of course, like any new property, it’ll be a while before we can rely on that behavior to work across a broad number of devices. Our biggest worry these days is iOS Safari with its incredibly annoying FOIT delay, so here’s hoping Apple gets on board with this new approach in an upcoming version!</p>
<p>For more on how that evolves, we’ll keep our eyes on this draft specification: <a href="https://github.com/KenjiBaheux/css-font-rendering">CSS Font Rendering</a>.</p>
<h2 id="thanks-for-reading-along!">Thanks for reading along! <a class="direct-link" href="#thanks-for-reading-along!" aria-hidden="true">#</a></h2>
<p>If you’re interested, we posted an example page on github to show how things are set up.</p>
<ul>
<li><a href="http://master-origin-font-loading.fgview.com/font-events.html">Font events demo page</a></li>
<li><a href="https://github.com/filamentgroup/font-loading/blob/master/font-events.html">Font events demo page source</a>.</li>
</ul>
Introducing Grunticon 2! Style & Animate your SVGs2015-02-04T00:00:00-05:00<h1>Introducing Grunticon 2! Style &amp; Animate your SVGs</h1>
<p>Posted by <a href="/about/#scott-jehl">Scott</a> on 02/04/2015</p>
<p>This week we’re very excited to release a new version of our Grunticon SVG workflow tool.
Version 2 of Grunticon makes it easy to embed SVG markup into your page so you can apply CSS and JavaScript to the actual SVG markup.</p>
<p><img src="/images/grunticon2-icons.gif" alt="screenshot of grunticon animated icons"></p>
<p>The bulk of the new features in version 2 take place in Grunticon’s JavaScript loader that’s meant to be included in your HTML, and we’ve been careful to preserve all existing features so you can upgrade without needing to change anything.</p>
<p>To celebrate the launch and show off the new features, we designed a site! Head on over to <a href="http://grunticon.com">Grunticon.com</a> to see some demos of all that it can do. We hope you like it!</p>
<p><a href="http://grunticon.com"><img src="/images/grunticon2.jpg" alt="screenshot of grunticon site"></a></p>
How we use web fonts responsibly, or, avoiding a @font-face-palm2015-01-08T00:00:00-05:00<h1>How we use web fonts responsibly, or, avoiding a @font-face-palm</h1>
<p>Posted by <a href="/about/#zach-leatherman">Zach</a> on 01/08/2015</p>
<p><strong>02/16/2015 Note:</strong> There’s an update to this article that recommends a slightly better approach. You can find it here: <a href="/lab/font-events.html">Font Loading Revisited with Font Events</a></p>
<p>Using <code>@font-face</code> to load custom web fonts is a great feature to give our sites a unique and memorable aesthetic. However, when you use custom fonts on the web using standard techniques, they can slow down page load speed and hamper performance—both real and perceived. Luckily, we’ve figured out some methods to apply them carefully to ensure your site correctly balances usability, performance and style.</p>
<h2 id="the-problem-with-%40font-face">The problem with <code>@font-face</code> <a class="direct-link" href="#the-problem-with-%40font-face" aria-hidden="true">#</a></h2>
<p>The CSS <code>@font-face</code> declaration is the standard approach for referencing custom fonts on the web:</p>
<pre><code>/* Define a custom web font */
@font-face {
font-family: 'MyWebFont';
url('webfont.woff2') format('woff2'),
url('webfont.woff') format('woff'),
url('webfont.ttf') format('truetype'),
}
/* Use that font in a page */
body {
font-family: 'MyWebFont', sans-serif;
}
</code></pre>
<p>Clean and simple, but unfortunately most browsers’ default handling of <code>@font-face</code> is problematic. When you reference an external web font using <code>@font-face</code>, most browsers will make any text that uses that font completely invisible while the external font is loading [Fig. 1, below]. Some browsers will wait a predetermined amount of time (usually three seconds) for the font to load before they give up and show the text using the fallback <code>font-family</code>. But just like a loyal puppy, WebKit browsers (Safari, default Android Browser, Blackberry) will wait <em>forever</em> (okay, often 30 seconds or more) for the font to return. This means your custom fonts represent a potential single point of failure for a usable site.</p>
<div class="figure-group fig-a">
<img src="/images/font-loading-blog-post/fastco.jpg" alt="A screenshot of Mobile Safari where the webfont is invisible">
<p class="caption"><small>FIG 1: Screenshot of a webpage loading in iOS Safari, with text invisible until custom fonts finish loading.</small></p>
</div>
<p>Even when the fonts do load correctly, custom fonts slow down the perceived speed of a site significantly because a page full of invisible text isn’t exactly usable. Sure, once the first page is visited, the custom fonts are cached and display quickly, but perceived speed for the <em>first</em> page view is critical. If we can’t paint a usable page within a few seconds, a lot of visitors will drop off.</p>
<p>For example, Fig. 2 shows a <a href="http://www.webpagetest.org/">webpagetest.org</a> timeline illustrating how <a href="http://www.filamentgroup.com/">filamentgroup.com</a> would look when accessed on a stable 3G connection if it were using the default font loading behavior, note that the custom <code>@font-face</code> text does not appear until a full second after first render:</p>
<p><a href="/images/font-loading-blog-post/default.png"><img src="/images/font-loading-blog-post/default-thumb.png" alt="A shaped 3G film strip showing how the fonts are invisible while loading"></a></p>
<p class="caption"><small>FIG 2: Timeline of our website using standard custom font loading. On a 3G cable connection, fonts delay by 1 full second.</small></p>
<p>Our users want a usable page as quickly as possible—within a second, ideally—so we want visible text as close to that goal as we can. There are several approaches you can take to work around these issues, but the most important thing you can do is to move away from the default way we’re told to load fonts.</p>
<p>Here are the criteria you should use when evaluating a font loading approach:</p>
<ul>
<li>The CSS request containing your <code>font-face</code> definition(s) should not block page render. Instead of referencing your fonts via <code>&lt;link&gt;</code>s in the <code>&lt;head&gt;</code> or via <code>@import</code> statements in an external stylesheet, try to load your fonts and font content asynchronously. Don’t worry, we’ll show you how.</li>
<li>Font requests should be set up to ensure the fallback text is visible while loading, avoiding the Flash of Invisible Text or FOIT.</li>
</ul>
<h2 id="the-filament-group-way%E2%84%A2-to-load-fonts">The Filament Group Way™ to load fonts <a class="direct-link" href="#the-filament-group-way%E2%84%A2-to-load-fonts" aria-hidden="true">#</a></h2>
<p>To optimize for the first view, we first make sure we have a native font in our <code>font-family</code> stack behind our custom web font, in this case <code>font-family: Open Sans, sans-serif;</code>. This sets the stage for how our text will render using the fallback experience while the font is loading using our new font loading method. JavaScript can then used to detect the best font format to use (WOFF2, WOFF, TTF) and asynchronously load a stylesheet that contains all the fonts embedded as a series of data URIs. This is a bit unconventional but it allows us to load all the custom fonts as a single HTTP request, which is nice both for minimizing reflows (all fonts arrive at once) and for reducing HTTP requests in general. To take this even further, after requesting a font, we set a cookie to flag that the custom fonts are now cached so we can avoid the flash of the default fonts on subsequent pages.</p>
<h3 id="step-1%3A-prepare-your-fonts">Step 1: Prepare your fonts <a class="direct-link" href="#step-1%3A-prepare-your-fonts" aria-hidden="true">#</a></h3>
<p>Custom fonts can be very heavy so the first order of business is minimizing the number of fonts we need to load in the first place. Remember each weight (regular, light, bold) and variant (regular italic, bold italic) of a typeface is a separate font file which can add up quickly. Try to keep the total number of custom fonts to less then five, but we usually shoot for 2-3 if we can.</p>
<p>To further streamline your font delivery, use a technique called subsetting that allows you to remove characters and symbols from a font that you don’t need. The <a href="http://www.fontsquirrel.com/tools/webfont-generator">FontSquirrel tool</a> makes this pretty easy.</p>
<h3 id="step-2%3A-prepare-the-font-stylesheets">Step 2: Prepare the font stylesheets <a class="direct-link" href="#step-2%3A-prepare-the-font-stylesheets" aria-hidden="true">#</a></h3>
<p>Encoding fonts to Data URIs</p>
<p>Let’s say we’re using the Open Sans typeface with two different weights: 400 and 700 (Bold). To support the widest range of browsers, we’ll need each font in three different formats: WOFF2, WOFF, and TrueType (TTF):</p>
<ul>
<li>OpenSans-Regular.ttf</li>
<li>OpenSans-Bold.ttf</li>
<li>OpenSans-Regular.woff</li>
<li>OpenSans-Bold.woff</li>
<li>OpenSans-Regular.woff2</li>
<li>OpenSans-Bold.woff2</li>
</ul>
<p>If you’re missing one or more of these formats, upload it into the <a href="http://www.fontsquirrel.com/tools/webfont-generator">Font Squirrel Web Font Generator</a> to create the others for you.</p>
<p>Take each of these font files and encode them into a Data URI so we can embed them into a stylesheet. If you aren’t familiar with how to create a Data URI, there are many options: <a href="http://compass-style.org/reference/compass/helpers/inline-data/#inline-font-files">SASS (Compass)</a>, <a href="http://css-tricks.com/snippets/php/create-data-uris/">PHP</a>, <a href="http://dopiaza.org/tools/datauri/index.php">online generators</a>, or by using OpenSSL on the command line (<code>openssl base64 -in filename.woff</code>).</p>
<p>Copy the output into three different stylesheets, one CSS file for each font format: WOFF2 (<code>data-woff2.css</code>), WOFF (<code>data-woff.css</code>), and TTF (<code>data-ttf.css</code> for Android). Here is what an example of <code>data-woff.css</code> might look like:</p>
<pre><code>@font-face {
font-family: Open Sans;
src: url(&quot;data:application/x-font-woff;charset=utf-8;base64,...&quot;) format(&quot;woff&quot;);
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: Open Sans;
src: url(&quot;data:application/x-font-woff;charset=utf-8;base64,...&quot;) format(&quot;woff&quot;);
font-weight: 700; /* Bold */
font-style: normal;
}
</code></pre>
<p>Inside of the other font format files, the <code>src: url(...) format(...)</code> should match up with the specific format. For example, inside <code>data-woff2.css</code> you’d use <code>url(&quot;data:application/font-woff2;charset=utf-8;base64,...&quot;) format(&quot;woff2&quot;);</code> and for <code>data-ttf.css</code> you’d use <code>url(&quot;data:application/x-font-ttf;charset=utf-8;base64,...&quot;) format(&quot;truetype&quot;);</code>.</p>
<h3 id="step-3%3A-set-up-the-stylesheet-loader">Step 3: Set up the Stylesheet loader <a class="direct-link" href="#step-3%3A-set-up-the-stylesheet-loader" aria-hidden="true">#</a></h3>
<p>Once a font file is prepared, we’ll need to load it asynchronously to ensure no FOIT occurs. We use our <a href="https://github.com/filamentgroup/loadCSS">our <code>loadCSS</code> utility</a> to handle this part. For example, here’s how we can use loadCSS to load our WOFF2 fonts:</p>
<pre><code>loadCSS( '/url/to/data-woff2.css' );
</code></pre>
<p>Of course, we want to load the appropriate font stylesheet for each browser that visits the site. How do we determine which format to use? By default we use the WOFF format because of its breadth of <a href="http://caniuse.com/#feat=woff">browser support</a>. If a browser passes a <a href="http://www.filamentgroup.com/lab/woff2.html">WOFF2 feature test</a>, we use WOFF2 instead because its file size is normally about 30% smaller. If we can reasonably guess that the current browser is the defualt Android Webkit Browser (not Chrome), we switch to TTF for Android 4.X support. Keep in mind that if an incorrect format is loaded, the browser will simply fallback to using default local fonts.</p>
<p>Here’s an excerpt of the JavaScript we use to load our fonts. We recommend placing this JavaScript inline in a <code>script</code> element in the <code>head</code> of your HTML to kick off the font request as soon as possible (<a href="https://github.com/filamentgroup/enhance">more about how we configure the head of our pages with Enhance.js</a>):</p>
<pre><code>// NOTE!! The WOFF2 feature test and loadCSS utility are omitted for brevity
var ua = window.navigator.userAgent;
// Use WOFF2 if supported
if( supportsWoff2 ) {
loadCSS( &quot;/url/to/data-woff2.css&quot; );
} else if( ua.indexOf( &quot;Android 4.&quot; ) &gt; -1 &amp;&amp; ua.indexOf( &quot;like Gecko&quot; ) &gt; -1 &amp;&amp; ua.indexOf( &quot;Chrome&quot; ) === -1 ) {
// Android's Default Browser needs TTF instead of WOFF
loadCSS( &quot;/url/to/data-ttf.css&quot; );
} else {
// Default to WOFF
loadCSS( &quot;/url/to/data-woff.css&quot; );
}
</code></pre>
<p>The browser will not make the text invisible while our Data URI CSS file is loading asynchronously. This means that the fallback text will be readable while our web fonts are loading—even if the request hangs and never returns.</p>
<p>Figure 3 below shows the change: With this technique we get readable immediately on first render. This is what we’re going for (3G timeline):</p>
<p><a href="/images/font-loading-blog-post/current.png"><img src="/images/font-loading-blog-post/current-thumb.png" alt="A shaped 3G film strip showing how the fallback is visible while the font is loading"></a></p>
<p class="caption"><small>FIG 3: Success! Timeline of our website using our recommended custom font loading. On a 3G cable connection, fonts appear on first render.</small></p>
<p>For more detail on this approach, we’ve also provided <a href="http://master-origin-font-loading.fgview.com/data-uris.html">a live demo</a> and the <a href="https://github.com/filamentgroup/font-loading/blob/master/data-uris.html">source code on GitHub</a>.</p>
<h2 id="using-cookies-to-make-this-smarter">Using Cookies to make this Smarter <a class="direct-link" href="#using-cookies-to-make-this-smarter" aria-hidden="true">#</a></h2>
<p>Up to this point, we’ve focused on preparing and loading our custom fonts responsibly so we can show the fallbfgack font while we wait for the custom fonts to load. When these fonts finally do load, the browser swaps out the native fonts for custom fonts. This will cause a repaint and usually has small layout shifts since the fonts are slightly different sizes. We think this font shift is a small tradeoff to show a usable page seconds faster on the initial visit but it can be annoying once you start navigating around.</p>
<p>To remedy this, we use cookies to track if the custom fonts are already downloaded and in the browser’s cache. If they are, we show the custom fonts right off the bat to avoid any shifting around.</p>
<p>Instead of the font loader above we’ll want to use a different loader, shown below. We’ll want to add a cookie to flag that the fonts are now cached. In addition to noting that the fonts are cached, the cookie also contains the URL to the specific font format being used as well (<code>data-woff2.css</code>, <code>data-woff.css</code>, or <code>data-ttf.css</code>). Don’t forget to include the <a href="https://github.com/filamentgroup/cookie">Filament Group cookie utility</a>:</p>
<pre><code>// NOTE!! The WOFF2 feature test, loadCSS, and cookie utility are omitted for brevity
// Default to WOFF
var fontFileUrl = &quot;/url/to/data-woff.css&quot;,
ua = window.navigator.userAgent;
// Use WOFF2 if supported
if( supportsWoff2 ) {
fontFileUrl = &quot;/url/to/data-woff2.css&quot;;
} else if( ua.indexOf( &quot;Android 4.&quot; ) &gt; -1 &amp;&amp; ua.indexOf( &quot;like Gecko&quot; ) &gt; -1 &amp;&amp; ua.indexOf( &quot;Chrome&quot; ) === -1 ) {
// Android's Default Browser needs TTF instead of WOFF
fontFileUrl = &quot;/url/to/data-ttf.css&quot;;
}
// ADDED: Make sure the fonts are not yet cached
if( fontFileUrl &amp;&amp; !cookie( &quot;fonts&quot; ) ) {
// Load the fonts asynchronously
loadCSS( fontFileUrl );
// ADDED: Set the cookie indicating the fonts are cached
// The cookie also denotes what format is used (WOFF, WOFF2, or TTF)
cookie( &quot;fonts&quot;, fontFileUrl, 7 );
}
</code></pre>
<p>Then add the following markup block to our <code>&lt;head&gt;</code> updating the values of each of the <code>fontsWOFF</code>, <code>fontsWOFF2</code>, <code>fontsTTF</code> variables with the URL of the Data URI CSS font format file. Note that when the cookie has been set and contains the value of the URL of the font format we want to load, a blocking <code>link</code> element is inserted into the page pointing to the Data URI CSS file. However, the blocking behavior of this request is okay because the CSS request has already been cached by the browser and it will load almost immediately.</p>
<pre><code>&lt;!--#set var="fontsWOFF" value="/css/data-woff.css" --&gt;
&lt;!--#set var="fontsWOFF2" value="/css/data-woff2.css" --&gt;
&lt;!--#set var="fontsTTF" value="/css/data-ttf.css" --&gt;
&lt;!--#if expr="$HTTP_COOKIE=/fonts\=$fontsWOFF/" --&gt;
&lt;link rel="stylesheet" href="&lt;!--#echo var="fontsWOFF" --&gt;"&gt;
&lt;!--#elif expr="$HTTP_COOKIE=/fonts\=$fontsWOFF2/" --&gt;
&lt;link rel="stylesheet" href="&lt;!--#echo var="fontsWOFF2" --&gt;"&gt;
&lt;!--#elif expr="$HTTP_COOKIE=/fonts\=$fontsTTF/" --&gt;
&lt;link rel="stylesheet" href="&lt;!--#echo var="fontsTTF" --&gt;"&gt;
&lt;!--#endif --&gt;</code></pre>
<p><em>The code above requires <a href="http://httpd.apache.org/docs/current/mod/mod_include.html#flowctrl">Apache Server Side Includes</a> but you could do something similar with any server side language.</em></p>
<p>For more detail on this approach using cookies, we’ve also provided <a href="http://master-origin-font-loading.fgview.com/data-uris-cookie.html">a live demo</a> and the <a href="https://github.com/filamentgroup/font-loading/blob/master/data-uris-cookie.html">source code on GitHub</a>.</p>
<h2 id="wrapping-up">Wrapping Up <a class="direct-link" href="#wrapping-up" aria-hidden="true">#</a></h2>
<p>Using web fonts can really be a great way to improve the quality of our web work, but using web fonts with the default loading behavior can be very detrimental to our page’s perceived performance. The above method works great to eliminate the text invisibility usually associated with <code>@font-face</code> and make our pages usable much faster. We hope you (and your visitors) find it useful!</p>
Researching the Performance costs of JavaScript MVC Frameworks2014-12-12T00:00:00-05:00<h1>Research: Performance Impact of Popular JavaScript MVC Frameworks</h1>
<p>Posted on 12/12/2014 by <a href="/about/#john-bender">John</a>, <a href="/about/#todd-parker">Todd</a>, &amp; <a href="/about/#scott-jehl">Scott</a></p>
<p>At Filament Group, we’ve been very focused on website performance. We’ve long been curious about the performance of popular JavaScript <a href="https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller">MVC frameworks</a> such as <a href="https://angularjs.org/">Angular</a>, <a href="http://backbonejs.org/">Backbone</a>, and <a href="http://emberjs.com/">Ember</a>, primarily because they offer elegant solutions to complex problems and we want to be able to use them in our own client work. These frameworks promise speed and developer convenience once up and running, but there is an initial cost in downloading, parsing, and executing a fairly large payload of JavaScript, particularly when the JavaScript is used to generate the HTML content for the site.</p>
<p class="note"><strong>Update:</strong> Data for React and Ampersand were added to this post’s graphic after we initially published it. That data can also be found in the <a href="https://docs.google.com/spreadsheets/d/136H3Aof5dDc8pSsGQ1VcCbPnwp_HldEcvnxU-ARd89M/edit?pli=1#gid=0">spreadsheet.</a></p>
<h2 id="speed-matters">Speed matters <a class="direct-link" href="#speed-matters" aria-hidden="true">#</a></h2>
<p>As an industry, we know that <em>perceived</em> website page load speed is critical to visitors. In <a href="http://www.aykira.com.au/2014/04/importance-website-loading-speed-top-3-factors-limit-website-speed/">one study</a>, 47% of users expected web pages to load in under two seconds and 57% said they will abandon a page if its load time is 3 seconds or more. Google even considers page speed <a href="http://googlewebmastercentral.blogspot.com/2010/04/using-site-speed-in-web-search-ranking.html">a factor in its search rankings</a>.</p>
<p>On a fast, reliable wifi connection, the wait for a usable page can often be minimal. But what happens in the browsing conditions that real users deal with every day?</p>
<blockquote>
<p>Statistics (from the GSMA) show that only 20% of N. America customers have LTE enabled handsets. Globally, the LTE penetration rate is ~5%. 3G networks are still the norm, not the exception - <a href="http://calendar.perfplanet.com/2014/bursting-our-bubble-mobile-performance-outside-the-first-world/">Bursting our Bubble: Mobile Performance Outside the First World</a></p>
</blockquote>
<p>While developers here in the US may enjoy speedy 4G/LTE connections, lower-end networks are still the norm—even in developed portions of the world. Heck, Scott here still has only a 2G connection speed at his home with T-Mobile! <em>[Insert Florida joke here]</em></p>
<p>But cell network latency (and Florida jokes) aside, mobile devices tend to be very slow at parsing and executing JavaScript as well. Google even suggests our <a href="https://docs.google.com/presentation/d/1MtDBNTH1g7CZzhwlJ1raEJagA8qM3uoV7ta6i66bO2M/present?slide=id.g3eb97ca8f_1860">sites should render within <em>one second</em></a>: can popular MVC frameworks deliver?</p>
<h3 id="initial-loading-time-matters">Initial loading time matters <a class="direct-link" href="#initial-loading-time-matters" aria-hidden="true">#</a></h3>
<p>One aspect where these frameworks often perform well is in <em>subsequent</em> page visits, as the frameworks are designed to re-render pages quickly with minimal server or network dependence. Of course, <em>initial</em> loading time (or the time it takes to load when you first visit a site) is arguably more important, especially in the age of links shared via social media: without a successful first visit, there are no subsequent visits.</p>
<p>What we wanted was a way to compare the initial load times of these frameworks in a more realistic manner than just looking at their file sizes.</p>
<h2 id="our-testing-setup">Our testing setup <a class="direct-link" href="#our-testing-setup" aria-hidden="true">#</a></h2>
<p>Fortunately, the <a href="http://todomvc.com/">TodoMVC project</a> has examples of the exact same application, built implemented across a wide range of popular JavaScript MVC frameworks. This provided us with a minimal, application: no second loads, few assets, and minimal logic to use in our tests.</p>
<p>The TodoMVC examples aren’t ‘production ready’ because they’re targeted at developers who want to see how the application is built with a given framework. To approximate a realistic production environment we pulled down the TodoMVC code samples, concatenated files, applied minification, served the assets with compression, and moved the host closer to the test location <a href="#foot-1">[1]</a>.</p>
<p>We ran our tests using <a href="http://WebPageTest.org">WebPageTest.org</a> and Andy Davies’ <a href="https://github.com/andydavies/WPT-Bulk-Tester">bulk testing spreadsheet</a>. We did 20 runs for each browser and connection, discarding extreme outliers <a href="#foot-2">[2]</a>. We tested a desktop browser (Chrome) and a mobile browser (Android Chrome) both at 3G, and the desktop again at Cable speeds, all from the Dulles, VA data center. The two desktop Chrome tests give us an opportunity to see the impact of different network conditions with the same browser; the Nexus 5 test gives us a view of what a modern Android phone with a 3G connection could experience.</p>
<h2 id="metric">Metric <a class="direct-link" href="#metric" aria-hidden="true">#</a></h2>
<p>Of all the numbers collected, we were most interested in <em>render start</em>. This is the moment in the WebPageTest timeline view where the Todo application’s UI first appears. It’s also the earliest point that a user could conceivably report the application “ready to use.” In other words, before the render start time, visitors will be staring at a blank screen; this filmstrip view of the Ember demo illustrates it nicely, simulating a Nexus 5 over 3G:</p>
<p><a href="http://www.webpagetest.org/video/compare.php?tests=141210_X3_AZA-r%3A1-c%3A0&amp;thumbSize=200&amp;ival=16.67&amp;end=visual"><img src="/images/initial-load-mvc/timeline.png" alt=""></a></p>
<h2 id="digression">Digression <a class="direct-link" href="#digression" aria-hidden="true">#</a></h2>
<p>It’s important to be clear about what the tests can and <em>can not</em> tell us about these frameworks.</p>
<p>First, these tests are intended to establish a reasonable “best case scenario” on initial load and render times for each framework, tested under three different network/device conditions. Chrome (the browser we used for the testing) is one of the fastest, most standards-compliant browsers in the world, and given how simple TodoMVC’s functionality is, it’s unlikely these frameworks will have significantly better initial load times for more feature-rich applications. So if the results here look too slow, a more complex application might be even slower.</p>
<p>Second, these tests are <em>not</em> a comparison of render start times <em>between</em> frameworks. Low render start times for a given framework for one application do <em>not</em> imply that all applications built with that framework will have low render start times. For example, it’s possible that the abstractions in Angular and Ember could reduce overall code size through re-use as application complexity increases.</p>
<p><strong>Update: we’ve since collected data for Ember 1.9, which can be viewed in the spreadsheet linked below. The numbers are comparable.</strong></p>
<h2 id="results">Results <a class="direct-link" href="#results" aria-hidden="true">#</a></h2>
<p>The <a href="https://docs.google.com/spreadsheets/d/136H3Aof5dDc8pSsGQ1VcCbPnwp_HldEcvnxU-ARd89M/edit?pli=1">data</a> is available for inspection, but the following stood out to us:</p>
<ul>
<li>Ember averages about 5 seconds on 3G on a Nexus 5 and about 3 seconds 3G in desktop Chrome.</li>
<li>Angular averages about 4 seconds on 3G on a Nexus 5 and about 3 seconds 3G in desktop Chrome.</li>
<li>Backbone appears to be the only framework with workable baseline performance on all connections.</li>
<li>Looking at the difference between the Nexus 5 and the Chrome desktop over 3G suggests that the execution time required to get the application on screen plays a significant roll in the overall render start performance for Angular and Ember <a href="#foot-3">[3]</a>.</li>
</ul>
<p><img src="/images/initial-load-mvc/todo-mvc-performance-summary-updated.png" alt="Average first render times:
Angular (51k script size) - PC, Cable (0.88 seconds) / PC, 3G (3.07 seconds) / Nexus 5, 3G (3.99 seconds),
Backbone (43k script size) - PC, Cable (0.29 seconds) / PC,3G (0.83 seconds) / Nexus 5, 3G (1.02 seconds),
Ember (165k script size) - PC, Cable (1.17 seconds) / PC,3G (3.21 seconds) / Nexus 5, 3G (5.00 seconds)" style="max-width:400px"></p>
<h2 id="conclusion">Conclusion <a class="direct-link" href="#conclusion" aria-hidden="true">#</a></h2>
<p>We encourage you to take a look at the tests and data for yourself and draw your own conclusions. At the very least, it suggests that if initial load times are an important criteria in your consideration of tools, you may not be able to make use many of these tools today.</p>
<p>But we have reason to believe that will improve in the future. There are <a href="/lab/performance-rwd.html">practical approaches we can already use today to reliably produce very fast rendering times</a>, but they work best when HTML content is delivered from the server side rather than generating it solely on the client. That approach benefits many areas of user experience aside from performance alone, and we plan to write more about that in a followup post.</p>
<p>But in the meantime, it’s incredibly encouraging to see that <a href="https://twitter.com/wycats/status/537042836084887552">the authors of frameworks like Ember are starting to acknowledge this issue</a>. We can’t wait to see where the next few months take us.</p>
<h3 id="footnotes">Footnotes <a class="direct-link" href="#footnotes" aria-hidden="true">#</a></h3>
<ol>
<li id="foot-1"><a href="https://github.com/johnbender/todomvc/tree/device-timing/server">Repository and test details</a>. In particular note that the host “Droplet” was in one of Digital Ocean’s NYC data centers in the interest of *simulating* CDN conditions. 80ms for TTFB on a cable connection is reasonable in our experience.</li>
<li id="foot-2">Extreme outliers here is defined as a 2× *worse*. This generally only happens when something goes wrong with the test.</li>
<li id="foot-3">Ideally we could test the Nexus 5 at faster connection speeds but that’s currently not an option on WebPageTest.org.</li>
</ol>
<p>Also, a special thanks goes to <a href="http://ethanmarcotte.com">Ethan Marcotte</a> for proofreading initial drafts of this post.</p></p>