{"version":"https://jsonfeed.org/version/1","title":"Aaron Gustafson: Content tagged mobile","description":"The latest 20 posts and links tagged mobile.","home_page_url":"https://www.aaron-gustafson.com","feed_url":"https://www.aaron-gustafson.com/feeds/mobile.json","author":{"name":"Aaron Gustafson","url":"https://www.aaron-gustafson.com"},"icon":"https://www.aaron-gustafson.com/i/og-logo.png","favicon":"https://www.aaron-gustafson.com/favicon.png","expired":false,"items":[{"id":"https://www.aaron-gustafson.com/notebook/rebuilding-a-php-app-using-isomorphic-javascript-with-eleventy-and-netlify/","title":"✍🏻 Rebuilding a PHP App using Isomorphic JavaScript with Eleventy &amp; Netlify","summary":"Back in the early days of the iPhone, I created tipr.mobi, a tip calculator that always produces a palindrome total. This is an overview of the minimal work I did to make it a modern web app that can run without a traditional back-end.","content_html":"<p>Back in the early days of the iPhone, I created <a href=\"https://tipr.mobi\">Tipr</a>, a tip calculator that always produces a palindrome total.<sup class=\"footnote-ref\"><a href=\"#fn1\" id=\"fnref1\">1</a></sup> This is an overview of the minimal work I did to make it a modern web app that can run without a traditional back-end.</p>\n<h2 id=\"what-i-had-to-work-with\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#what-i-had-to-work-with\" aria-hidden=\"true\">#</a> What I had to work with</h2>\n<p>The previous iteration of Tipr was built in my hotel room while I was on site doing some consulting for a certain Silicon Valley company. I was rocking a <a href=\"https://wikipedia.org/wiki/Treo_650\">Palm Treo 650</a> at the time and that day a few of my colleagues had lined up to wait for the release of <a href=\"https://wikipedia.org/wiki/IPhone_(1st_generation)\">the very first iPhone</a>. At the time, web apps were the only way to get an “app” on the iPhone as there was no SDK or even an App Store.</p>\n<figure id=\"2023-02-02-02\">\n<p><img src=\"https://www.aaron-gustafson.com/i/posts/2023-02-03/iphone.jpg\" alt=\"\" width=\"696\" height=\"928\"></p>\n<figcaption>Tipr on the 1st generation iPhone, in the hands of Micah Alpern, June 2007.</figcaption>\n</figure>\n<p>I did a lot of PHP development back in the day, so armed with all of the mobile web development best practices of the day, I set about building the site and making it speedy. Some of the notable features of Tipr included:</p>\n<ul>\n<li>Inlining CSS &amp; JS file contents into the HTML.</li>\n<li>Using <a href=\"https://www.php.net/manual/en/book.outcontrol.php\">PHP output buffers</a> to compress the HTML on the server before sending it over the wire.</li>\n<li>Server side processing in PHP.</li>\n<li>Client side processing via <abbr aria-label=\"XMLHttpRequest\" title=\"XMLHttpRequest\">XHR</abbr> to a PHP-driven API.</li>\n</ul>\n<p>At the time, most of these approaches were very new. As an industry, we weren’t doing a whole lot to ensure peak performance on mobile because most people’s mobile browsers were pretty crappy. This was the heyday of Usablenet’s “mobile friendly” bolt-on and WAP. Then came Mobile Safari.</p>\n<figure id=\"2023-02-02-03\">\n<p><img src=\"https://www.aaron-gustafson.com/i/posts/2023-02-03/app-store.png\" alt=\"\" width=\"696\" height=\"472\"></p>\n<figcaption>Tipr in the original Apple App Store, back when web apps were first class citizens on iPhone OS.</figcaption>\n</figure>\n<h2 id=\"a-lot-has-changed-since-2007\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#a-lot-has-changed-since-2007\" aria-hidden=\"true\">#</a> A lot has changed since 2007</h2>\n<p>The Tipr site has remained largely untouched since I built it in the Summer of 2007. That October, I added a theme switcher that made the site <a href=\"https://pinkforoctober.org/\">pink for October</a> (Breast Cancer Awareness Month). I added a free text message-based interface using the then-free <a href=\"https://www.textmarks.com/\">TextMarks</a> service and <a href=\"https://twitter.com/tipr\">a Twitter bot</a> as well. But as far as the web interface went, it remained largely untouched.</p>\n<p>Here are a handful of things that have come to the web in the intervening years:</p>\n<ul>\n<li>HTML5 Form Validation API</li>\n<li>SVG support</li>\n<li>CSS3</li>\n<li>Media Queries</li>\n<li>Web App Manifest</li>\n<li>Service Worker (and its precursor the AppCache)</li>\n<li>Flexbox</li>\n<li>CSS Grid</li>\n</ul>\n<p>Phew, that’s a lot! While I haven’t made upgrades in all these areas, I did sprinkle in a few, mainly to make it a true PWA and boost performance.</p>\n<h2 id=\"moving-from-php-to-a-%E2%80%9Cstatic%E2%80%9D-site\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#moving-from-php-to-a-%E2%80%9Cstatic%E2%80%9D-site\" aria-hidden=\"true\">#</a> Moving from PHP to a “static” site</h2>\n<p>Much of my work over the last few years has been in the world of static site generators (e.g., <a href=\"https://jekyllrb.com/\">Jekyll</a>, <a href=\"https://www.11ty.dev/\">Eleventy</a>). I’m quite enamored of <a href=\"https://www.11ty.dev/\">Eleventy</a>, having used it for a number of projects at this point. Since I know it really well, it made sense to use it for this project too. The installation steps are minimal and I already had a library of configuration options, plugins, and filters to roll with.</p>\n<p>While in the process of migrating to Eleventy, I also took the opportunity to</p>\n<ul>\n<li>Swap raster graphics for SVGs,</li>\n<li>Set up a Web App Manifest,</li>\n<li>Add a Service Worker, and</li>\n<li>Update the site’s <code>meta</code> info to reflect current best practices.</li>\n</ul>\n<p>I also swapped out the PHP logic that governed the pink color theme for <a href=\"https://github.com/aarongustafson/tipr.mobi/blob/main/src/_includes/layouts/base.njk#L7-L12\">a simple <code>script</code> in the <code>head</code> of the every page</a>. Since the color change is an enhancement, rather than a necessity, I didn’t feel like it was something I needed to manage another way.</p>\n<p>The greatest challenge in moving Tipr over to a static site was setting up the tip calculation engine, which had been in PHP to ensure it would work even if JavaScript wasn’t available.</p>\n<h2 id=\"migrating-the-core-logic-to-isomorphic-javascript\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#migrating-the-core-logic-to-isomorphic-javascript\" aria-hidden=\"true\">#</a> Migrating the core logic to isomorphic JavaScript</h2>\n<p>When I originally built Tipr, JavaScript on the back-end wasn’t a thing. That’s why the core tip calculation engine was built in PHP. At the time, even <abbr aria-label=\"XMLHttpRequest\" title=\"XMLHttpRequest\">XHR</abbr> was in its infancy, so the fact that I could use PHP to do the calculations for both the server-side—for when JavaScript wasn’t available—and client-side—when it was—was pretty amazing.</p>\n<p>Today, JavaScript is ubiquitous across the whole stack, which made it the logical choice for building out the revised tip calculator. As with the original, I needed the calculation to work on the client side if it could—saving a round trip to the server—but to also have the ability to fall back to a traditional form submission if the client-side approach wasn’t feasible. That would be possible by having client-side JavaScript for the form itself, with the server-side piece handled by <a href=\"https://docs.netlify.com/edge-functions/overview/\">Netlify’s Edge Functions</a> (integrated through <a href=\"https://www.11ty.dev/blog/eleventy-edge/\">Eleventy’s Edge plugin</a>).</p>\n<p>From an architectural standpoint, I really didn’t want to have my logic duplicated in each place, so I began to play around with ensconcing the calculation logic in a JavaScript include, so I could import it into the form page itself <em>and</em> a JavaScript module that the Edge Function could use.</p>\n<p>You can view <a href=\"https://github.com/aarongustafson/tipr.mobi\">Tipr’s source on GitHub</a>, but here’s a basic rundown of the relevant directories and files:</p>\n<pre class=\"language-txt\" tabindex=\"0\"><code class=\"language-txt\">netlify\n  edge-functions\n    tipr.js\nsrc\n  _includes\n    js\n      tipr.js\n  j\n    process.njk\n  index.html\nnetlify.toml</code></pre>\n<h3 id=\"src%2F_includes%2Fjs%2Ftipr.js\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#src%2F_includes%2Fjs%2Ftipr.js\" aria-hidden=\"true\">#</a> <code>src/_includes/js/tipr.js</code></h3>\n<p>This file contains the central logic of the tip calculator. It’s written in vanilla JavaScript with the intent that it would be understandable by the widest possible assortment of browsers out there.</p>\n<h3 id=\"src%2Findex.html\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#src%2Findex.html\" aria-hidden=\"true\">#</a> <code>src/index.html</code></h3>\n<p>The homepage of the site is also home to the tip calculation form. Below the form is an embedded <code>script</code> element containing the logic for interacting with the DOM for the client-side version of the tip calculator. I include the logic at the top of that <code>script</code>:</p>\n<pre class=\"language-njk\" tabindex=\"0\"><code class=\"language-njk\"><span class=\"token operator\">&lt;</span><span class=\"token variable\">script</span><span class=\"token operator\">></span>\n  <span class=\"token punctuation\">{</span><span class=\"token operator\">%</span> <span class=\"token variable\">include</span> <span class=\"token string\">\"js/tipr.js\"</span> <span class=\"token operator\">%</span><span class=\"token punctuation\">}</span>\n\n  <span class=\"token operator\">//</span> <span class=\"token variable\">The</span> <span class=\"token variable\">rest</span> <span class=\"token variable\">of</span> <span class=\"token variable\">the</span> <span class=\"token variable\">JavaScript</span> <span class=\"token variable\">logic</span>\n<span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span><span class=\"token variable\">script</span><span class=\"token operator\">></span>\n</code></pre>\n<h3 id=\"src%2Fj%2Fprocess.njk\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#src%2Fj%2Fprocess.njk\" aria-hidden=\"true\">#</a> <code>src/j/process.njk</code></h3>\n<p>This file exists solely to export the JavaScript logic from the include in a way that it can be consumed by the Edge Function. It will render a new JavaScript file called “process.js” and turns the central processing logic into a JavaScript module that <a href=\"https://deno.land/\">Deno</a> can use (Deno powers Netlify’s Edge Functions):</p>\n<pre class=\"language-njk\" tabindex=\"0\"><code class=\"language-njk\"><span class=\"token operator\">-</span><span class=\"token operator\">-</span><span class=\"token operator\">-</span>\n<span class=\"token variable\">layout</span><span class=\"token punctuation\">:</span> <span class=\"token boolean\">false</span>\n<span class=\"token variable\">permalink</span><span class=\"token punctuation\">:</span> <span class=\"token operator\">/</span><span class=\"token variable\">j</span><span class=\"token operator\">/</span><span class=\"token variable\">process</span><span class=\"token punctuation\">.</span><span class=\"token variable\">js</span>\n<span class=\"token operator\">-</span><span class=\"token operator\">-</span><span class=\"token operator\">-</span>\n\n<span class=\"token punctuation\">{</span><span class=\"token operator\">%</span> <span class=\"token variable\">include</span> <span class=\"token string\">\"js/tipr.js\"</span> <span class=\"token operator\">%</span><span class=\"token punctuation\">}</span>\n\n<span class=\"token variable\">export</span> <span class=\"token punctuation\">{</span> <span class=\"token variable\">process</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<h3 id=\"netlify%2Fedge-functions%2Ftipr.js\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#netlify%2Fedge-functions%2Ftipr.js\" aria-hidden=\"true\">#</a> <code>netlify/edge-functions/tipr.js</code></h3>\n<p>We define Edge Functions for use with Netlify in the <code>netlify/edge-functions</code> folder. To make use of the core JavaScript logic in the Edge Function, I can import it from the module created above before using it in the function itself:</p>\n<pre class=\"language-js\" tabindex=\"0\"><code class=\"language-js\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> process <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"./../../_site/j/process.js\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">setCookie</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">context<span class=\"token punctuation\">,</span> name<span class=\"token punctuation\">,</span> value</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  context<span class=\"token punctuation\">.</span>cookies<span class=\"token punctuation\">.</span><span class=\"token function\">set</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    name<span class=\"token punctuation\">,</span>\n    value<span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">path</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">httpOnly</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">secure</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">sameSite</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Lax\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">export</span> <span class=\"token keyword\">default</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">request<span class=\"token punctuation\">,</span> context</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">let</span> url <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">URL</span><span class=\"token punctuation\">(</span>request<span class=\"token punctuation\">.</span>url<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token comment\">// Save to cookie, redirect back to form</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">.</span>pathname <span class=\"token operator\">===</span> <span class=\"token string\">\"/process/\"</span> <span class=\"token operator\">&amp;&amp;</span> request<span class=\"token punctuation\">.</span>method <span class=\"token operator\">===</span> <span class=\"token string\">\"POST\"</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span> request<span class=\"token punctuation\">.</span>headers<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"content-type\"</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">===</span> <span class=\"token string\">\"application/x-www-form-urlencoded\"</span> <span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">let</span> body <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> request<span class=\"token punctuation\">.</span><span class=\"token function\">clone</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">formData</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">let</span> postData <span class=\"token operator\">=</span> Object<span class=\"token punctuation\">.</span><span class=\"token function\">fromEntries</span><span class=\"token punctuation\">(</span>body<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token keyword\">let</span> result <span class=\"token operator\">=</span> <span class=\"token function\">process</span><span class=\"token punctuation\">(</span> postData<span class=\"token punctuation\">.</span>check<span class=\"token punctuation\">,</span> postData<span class=\"token punctuation\">.</span>percent <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token function\">setCookie</span><span class=\"token punctuation\">(</span> context<span class=\"token punctuation\">,</span> <span class=\"token string\">\"check\"</span><span class=\"token punctuation\">,</span> result<span class=\"token punctuation\">.</span>check <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token function\">setCookie</span><span class=\"token punctuation\">(</span> context<span class=\"token punctuation\">,</span> <span class=\"token string\">\"tip\"</span><span class=\"token punctuation\">,</span> result<span class=\"token punctuation\">.</span>tip <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token function\">setCookie</span><span class=\"token punctuation\">(</span> context<span class=\"token punctuation\">,</span> <span class=\"token string\">\"total\"</span><span class=\"token punctuation\">,</span> result<span class=\"token punctuation\">.</span>total <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n      <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Response</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">null</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">status</span><span class=\"token operator\">:</span> <span class=\"token number\">302</span><span class=\"token punctuation\">,</span>\n        <span class=\"token literal-property property\">headers</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token literal-property property\">location</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/results/\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">return</span> context<span class=\"token punctuation\">.</span><span class=\"token function\">next</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n</code></pre>\n<p>What’s happening here is that when a request comes in to this Edge Function, the default export will be executed. Most of this code is directly lifted from <a href=\"https://edge-functions-examples.netlify.app/\">Netlify’s Edge Functions demo site</a>. I grab the form data, pass it into the <code>process</code> function, and then set browser cookies for each of the returned values before redirecting the request to <a href=\"https://github.com/aarongustafson/tipr.mobi/blob/main/src/results.html\">the result page</a>.</p>\n<p>On that page, I use Eleventy’s Edge plugin to render the check, tip, and total amounts:</p>\n<pre class=\"language-njk\" tabindex=\"0\"><code class=\"language-njk\"><span class=\"token delimiter punctuation\">{%</span> <span class=\"token tag keyword\">edge</span> <span class=\"token operator\">%</span><span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">{</span><span class=\"token operator\">%</span> <span class=\"token variable\">set</span> <span class=\"token variable\">check</span> <span class=\"token operator\">=</span> <span class=\"token variable\">eleventy</span><span class=\"token punctuation\">.</span><span class=\"token variable\">edge</span><span class=\"token punctuation\">.</span><span class=\"token variable\">cookies</span><span class=\"token punctuation\">.</span><span class=\"token variable\">check</span> <span class=\"token operator\">%</span><span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">{</span><span class=\"token operator\">%</span> <span class=\"token variable\">set</span> <span class=\"token variable\">tip</span> <span class=\"token operator\">=</span> <span class=\"token variable\">eleventy</span><span class=\"token punctuation\">.</span><span class=\"token variable\">edge</span><span class=\"token punctuation\">.</span><span class=\"token variable\">cookies</span><span class=\"token punctuation\">.</span><span class=\"token variable\">tip</span> <span class=\"token operator\">%</span><span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">{</span><span class=\"token operator\">%</span> <span class=\"token variable\">set</span> <span class=\"token variable\">total</span> <span class=\"token operator\">=</span> <span class=\"token variable\">eleventy</span><span class=\"token punctuation\">.</span><span class=\"token variable\">edge</span><span class=\"token punctuation\">.</span><span class=\"token variable\">cookies</span><span class=\"token punctuation\">.</span><span class=\"token variable\">total</span> <span class=\"token operator\">%</span><span class=\"token punctuation\">}</span>\n  <span class=\"token operator\">&lt;</span><span class=\"token variable\">tr</span> <span class=\"token variable\">id</span><span class=\"token operator\">=</span><span class=\"token string\">\"check\"</span><span class=\"token operator\">></span>\n    <span class=\"token operator\">&lt;</span><span class=\"token variable\">th</span> <span class=\"token variable\">scope</span><span class=\"token operator\">=</span><span class=\"token string\">\"row\"</span><span class=\"token operator\">></span><span class=\"token variable\">Check</span><span class=\"token operator\">&amp;</span><span class=\"token variable\">nbsp</span><span class=\"token punctuation\">;</span><span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span><span class=\"token variable\">th</span><span class=\"token operator\">></span>\n    <span class=\"token operator\">&lt;</span><span class=\"token variable\">td</span><span class=\"token operator\">></span>$<span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span> <span class=\"token variable\">check</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span><span class=\"token variable\">td</span><span class=\"token operator\">></span>\n  <span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span><span class=\"token variable\">tr</span><span class=\"token operator\">></span>\n  <span class=\"token operator\">&lt;</span><span class=\"token variable\">tr</span> <span class=\"token variable\">id</span><span class=\"token operator\">=</span><span class=\"token string\">\"tip\"</span><span class=\"token operator\">></span>\n    <span class=\"token operator\">&lt;</span><span class=\"token variable\">th</span> <span class=\"token variable\">scope</span><span class=\"token operator\">=</span><span class=\"token string\">\"row\"</span><span class=\"token operator\">></span><span class=\"token variable\">Tip</span><span class=\"token operator\">&amp;</span><span class=\"token variable\">nbsp</span><span class=\"token punctuation\">;</span><span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span><span class=\"token variable\">th</span><span class=\"token operator\">></span>\n    <span class=\"token operator\">&lt;</span><span class=\"token variable\">td</span><span class=\"token operator\">></span>$<span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span> <span class=\"token variable\">tip</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span><span class=\"token variable\">td</span><span class=\"token operator\">></span>\n  <span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span><span class=\"token variable\">tr</span><span class=\"token operator\">></span>\n  <span class=\"token operator\">&lt;</span><span class=\"token variable\">tr</span> <span class=\"token variable\">id</span><span class=\"token operator\">=</span><span class=\"token string\">\"total\"</span><span class=\"token operator\">></span>\n    <span class=\"token operator\">&lt;</span><span class=\"token variable\">th</span> <span class=\"token variable\">scope</span><span class=\"token operator\">=</span><span class=\"token string\">\"row\"</span><span class=\"token operator\">></span><span class=\"token variable\">Total</span><span class=\"token operator\">&amp;</span><span class=\"token variable\">nbsp</span><span class=\"token punctuation\">;</span><span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span><span class=\"token variable\">th</span><span class=\"token operator\">></span>\n    <span class=\"token operator\">&lt;</span><span class=\"token variable\">td</span><span class=\"token operator\">></span>$<span class=\"token punctuation\">{</span><span class=\"token punctuation\">{</span> <span class=\"token variable\">total</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">}</span><span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span><span class=\"token variable\">td</span><span class=\"token operator\">></span>\n  <span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span><span class=\"token variable\">tr</span><span class=\"token operator\">></span>\n<span class=\"token punctuation\">{</span><span class=\"token operator\">%</span> <span class=\"token variable\">endedge</span> <span class=\"token operator\">%</span><span class=\"token punctuation\">}</span>\n</code></pre>\n<p>Side note: The cookies get reset using <a href=\"https://github.com/aarongustafson/tipr.mobi/blob/main/netlify/edge-functions/reset.js\">a separate Edge Function</a>.</p>\n<h3 id=\"netlify.toml\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#netlify.toml\" aria-hidden=\"true\">#</a> <code>netlify.toml</code></h3>\n<p>To wire up the Edge Functions, we use put a <code>netlify.toml</code> file in the root of the project. Configuration is pretty straightforward: you tell it the Edge Function you want to use and the path to associate it with. You can choose to associate it with a unique path or run the Edge Function on every path.</p>\n<p>Here’s an excerpt from <a href=\"https://github.com/aarongustafson/tipr.mobi/blob/main/netlify.toml\">Tipr’s <code>netlify.toml</code></a> as it pertains to the Edge Function above:</p>\n<pre class=\"language-toml\" tabindex=\"0\"><code class=\"language-toml\"><span class=\"token punctuation\">[</span><span class=\"token punctuation\">[</span><span class=\"token table class-name\">edge_functions</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span>\n<span class=\"token key property\">function</span> <span class=\"token punctuation\">=</span> <span class=\"token string\">\"tipr\"</span>\n<span class=\"token key property\">path</span> <span class=\"token punctuation\">=</span> <span class=\"token string\">\"/process/\"</span></code></pre>\n<p>This tells Netlify to route requests to <code>/process/</code> through <code>netlify/edge-functions/tipr.js</code>. Then all that was left to do was wire up the <code>form</code> to use that endpoint as its <code>action</code>:</p>\n<pre class=\"language-html\" tabindex=\"0\"><code class=\"language-html\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>form</span> <span class=\"token attr-name\">id</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>calc<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">method</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>post<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">action</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>/process/<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>form</span><span class=\"token punctuation\">></span></span></code></pre>\n<h3 id=\"isomorphic-edges\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#isomorphic-edges\" aria-hidden=\"true\">#</a> Isomorphic Edges</h3>\n<p>It took a fair bit of time to figure this all out, but I’m pretty excited by the possibilities of this approach for building more static isomorphic apps. Oh, and the new site… <a href=\"https://speedlify.aaron-gustafson.com/apps/\">is fast</a>.</p>\n<hr class=\"footnotes-sep\">\n<section class=\"footnotes\">\n<h4 class=\"hidden\">Footnotes</h4>\n<ol class=\"footnotes-list\">\n<li id=\"fn1\" class=\"footnote-item\"><p>Why a palindrome? Well, it makes it pretty easy to detect tip fraud because all restaurant totals will always be the same forwards &amp; backwards. It’s a little easier than a checksum. <a href=\"#fnref1\" class=\"footnote-backref\">↩︎</a></p>\n</li>\n</ol>\n</section>\n","social_text":"Wherein I migrate @tipr from a #PHP-based mobile web app to a #serverless #PWA.","url":"https://www.aaron-gustafson.com/notebook/rebuilding-a-php-app-using-isomorphic-javascript-with-eleventy-and-netlify/","tags":["mobile","progressive enhancement","progressive web apps","web development"],"image":"https://www.aaron-gustafson.com/i/posts/2023-02-03/hero.jpg","date_published":"2023-02-03T20:40:59Z"},{"id":"https://www.aaron-gustafson.com/notebook/links/accessibility-features/","title":"🔗 Accessibility features","content_html":"<p>Interesting study of ~2 million Dutch smartphone users reveals how many people are using accessibility features on their phones (and which ones). It’s more than you think.</p>\n","social_text":"Interesting study of ~2 million Dutch smartphone users reveals how many people are using accessibility features on their phones (and which ones). It’s more than you think.","url":"https://www.aaron-gustafson.com/notebook/links/accessibility-features/","external_url":"https://appt.org/en/features","tags":["accessibility","mobile"],"image":"https://beta.appt.org/share.png","date_published":"2022-09-28T16:01:55Z"},{"id":"https://www.aaron-gustafson.com/notebook/links/mobile-devs-making-the-same-security-mistakes-web-devs-made-in-the-early-2000s/","title":"🔗 Mobile Devs Making the Same Security Mistakes Web Devs Made in the Early 2000s","content_html":"<p>Anyone who’s seen one of my forms talks knows how adamant I am about this: you can never trust the client! Doesn’t matter if that client is a web browser or your mobile app.</p>\n","social_text":"Anyone who’s seen one of my forms talks knows how adamant I am about this: you can never trust the client! Doesn’t matter if that client is a web browser or your mobile app.","url":"https://www.aaron-gustafson.com/notebook/links/mobile-devs-making-the-same-security-mistakes-web-devs-made-in-the-early-2000s/","external_url":"https://www.bleepingcomputer.com/news/security/mobile-devs-making-the-same-security-mistakes-web-devs-made-in-the-early-2000s/","tags":["mobile","security"],"image":"https://www.bleepstatic.com/content/posts/2018/06/04/phone-apps.jpg","date_published":"2018-06-27T20:11:03Z"},{"id":"https://www.aaron-gustafson.com/speaking-engagements/designing-for-everyone-building-great-web-experiences-for-any-device/","title":"📢 Designing for Everyone: Building Great Web Experiences for Any Device","summary":"In this session, I help you think about user experience from its foundation, so you understand what it means to build adaptive web experiences – whether in an app, in a browser, or beyond.","content_html":"<p>As the capabilities of the web platform advance, and the pervasiveness of web content proliferates, fundamental design principles are more important than ever: How do you enhance your experience as new capabilities become available in Microsoft Edge and other browsers? You’ve considered how your content looks and operates on mobile, but how does that design adapt to desktop devices? Is it usable via “headless” interfaces like screen readers or Cortana? Does your content adapt gracefully to the constraints of its environment?</p>\n<p>In this session, I help you think about user experience from its foundation, so you understand what it means to build adaptive web experiences – whether in an app, in a browser, or beyond. We’ll show you some of the newest capabilities coming to Microsoft Edge that let you build differentiated experiences on Windows 10. You’ll walk away practical, cross-platform code examples you can put to use in your projects today, and foundational principles to ensure that your projects are ready for anything.</p>\n","social_text":"In this session, I help you think about user experience from its foundation, so you understand what it means to build adaptive web experiences – whether in an app, in a browser, or beyond.","url":"https://www.aaron-gustafson.com/speaking-engagements/designing-for-everyone-building-great-web-experiences-for-any-device/","tags":["progressive web apps","mobile","performance","progressive enhancement","responsive web design"],"image":"https://www.aaron-gustafson.com/undefined","date_published":"2018-05-07T13:00:00Z"},{"id":"https://www.aaron-gustafson.com/notebook/links/remember-palms-webos-maybe-not-but-apple-and-google-definitely-do/","title":"🔗 Remember Palm’s WebOS? Maybe not, but Apple and Google definitely do","content_html":"<p>WebOS… so far ahead of its time:</p>\n<ul>\n<li>Web code for apps (see PWAs),</li>\n<li>Multiple synchronized calendars,</li>\n<li>Unified social media &amp; contact management,</li>\n<li>Curved displays</li>\n<li>Wireless charging</li>\n<li>Integrated text and Web messaging</li>\n<li>Unintrusive notifications</li>\n<li>“Cards” for running apps</li>\n<li>No home button (it used swipe instead)</li>\n</ul>\n<p>And all of these features were available <strong>eight years ago</strong>!</p>\n","url":"https://www.aaron-gustafson.com/notebook/links/remember-palms-webos-maybe-not-but-apple-and-google-definitely-do/","external_url":"https://www.salon.com/2017/09/03/remember-palms-webos-maybe-not-but-apple-and-google-definitely-do/","tags":["user experience","mobile"],"image":"https://mediaproxy.salon.com/width/1200/https://media.salon.com/2017/09/pre.jpg","date_published":"2017-09-12T15:48:04Z"},{"id":"https://www.aaron-gustafson.com/notebook/links/freedom-251-maker-says-2-lakh-rs-251-phones-ready-will-launch-cheapest-hd-led-tv-853073/","title":"🔗 Freedom 251 Maker Says 2 Lakh Rs. 251 Phones Ready, Will Launch &#39;Cheapest&#39; HD LED TV","content_html":"<p>The world’s cheapest smartphone (251 Indian Rupees—less than US$4) is here. The specs:</p>\n<ul>\n<li>4-inch screen</li>\n<li>dual-SIM</li>\n<li>3G</li>\n<li>1.3GHz quad-core processor</li>\n<li>1GB of RAM</li>\n<li>8GB of internal storage (supporting memory cards of up to 32GB)</li>\n<li>8-megapixel rear camera with flash</li>\n<li>3.2-megapixel front camera</li>\n<li>1800mAh battery</li>\n<li>Android 5.1 (Lollipop)</li>\n</ul>\n<p><a href=\"https://tech.firstpost.com/news-analysis/ringing-bells-has-actually-shipped-5000-freedom-251-devices-but-critics-need-not-apologise-324579.html\">The rollout is slow and units are being sold at a loss</a>, but still. This is amazing and the program, which is <a href=\"https://tech.firstpost.com/news-analysis/freedom-251-2-lakh-units-to-be-shipped-via-state-wise-lucky-draw-between-30-june-to-5-july-322588.html\">subsidized by the Indian government</a>, will bring the web to millions who have not been able to access it previously.</p>\n","url":"https://www.aaron-gustafson.com/notebook/links/freedom-251-maker-says-2-lakh-rs-251-phones-ready-will-launch-cheapest-hd-led-tv-853073/","external_url":"https://gadgets.ndtv.com/mobiles/news/freedom-251-maker-says-2-lakh-rs-251-phones-ready-will-launch-cheapest-hd-led-tv-853073","tags":["Android","mobile"],"image":"https://cdn.ndtv.com/tech/images/gadgets/freedom_251_14_ndtv.jpg","date_published":"2016-07-13T14:12:42Z"},{"id":"https://www.aaron-gustafson.com/notebook/links/google-chrome-android-data-saver-image-blocking/","title":"🔗 Chrome for Android can now save more data by blocking website images","summary":"Full circle, eh? The Blazer browser on my Palm Treo 650 had a “Lightning Mode” that switched off (optionally) images, CSS, and JavaScript.","content_html":"<p>Full circle, eh? <a href=\"https://en.wikipedia.org/wiki/Blazer_(web_browser)\">The Blazer browser</a> on my <a href=\"https://en.wikipedia.org/wiki/Treo_650\">Palm Treo 650</a> (circa 2004) had a “Fast Mode” that switched off (optionally) images, CSS, and JavaScript.</p>\n","url":"https://www.aaron-gustafson.com/notebook/links/google-chrome-android-data-saver-image-blocking/","external_url":"https://www.theverge.com/2015/12/1/9827386/google-chrome-android-data-saver-image-blocking","tags":["Android","mobile","performance"],"image":"https://cdn.vox-cdn.com/thumbor/hH7yWh5QlKVwM0cvVwiAVl8aC8E=/0x122:2039x1269/1600x900/cdn.vox-cdn.com/uploads/chorus_image/image/47776499/chrome1_2040.0.0.jpg","date_published":"2015-12-01T19:42:27Z"},{"id":"https://www.aaron-gustafson.com/notebook/bringing-sanity-and-order-to-device-testing/","title":"✍🏻 Bringing Sanity and Order to Device Testing","summary":"With the constant onslaught of new devices, form factors, and considerations, it’s tempting to throw up our hands and find another line of work. Thankfully, however, all is not lost.","content_html":"<p>It seems like every other day the public is granted some new means of accessing the web. Some days it’s <a href=\"http://www.engadget.com/2015/04/29/microsoft-edge/\">a new browser</a>. Others it’s <a href=\"http://www.pcadvisor.co.uk/reviews/mobile-phone/3504276/yotaphone-2-review-uk-dual-screen-smartphone/\">a new smartphone</a>. Or <a href=\"http://www.engadget.com/products/microsoft/surface/3/\">a tablet</a>. Or <a href=\"http://the-digital-reader.com/2014/10/22/voyage-vs-paperwhite-comparison-review-web-browser/\">an e-reader</a>. Or <a href=\"https://www.nintendo.com/3ds/internetbrowser/specs/\">a video game console</a>. Or <a href=\"http://www.phonearena.com/news/Surf-the-Internet-from-your-Android-Wear-smartwatch-Now-possible-via-this-new-app_id58580\">a smartwatch</a>. Or <a href=\"https://html5test.com/compare/browser/samsungsmarttv-2013.html\">a TV</a>. Or <a href=\"http://www.techradar.com/us/reviews/wearables/microsoft-hololens-1281834/review\">a heads-up display</a>. Or <a href=\"http://www.popularmechanics.com/cars/a13191/the-future-of-car-connectivity-is-a-real-web-browser-in-the-dash-17416796/\">a car</a>. Or <a href=\"http://www.lgblog.lt/2009/10/28/zvilgsnis-atgal-pirmasis-pasaulyje-saldytuvas-su-internetu-lg-internet-digital-dios/\">a refrigerator</a>.</p>\n<p>I worked on one project where the client provided me with a spreadsheet detailing 1,400 different user agents that accessed the login screen for the m-dot site. In two days!</p>\n<p>As further evidence, consider <a href=\"http://blog.jasonsamuels.net/post/21633531278/analytics-confirm-the-need-for-adaptive-web-design\">the enlightening details of this post from Jason Samuels</a> of the National Council on Family relations, a non-profit organization:</p>\n<ul>\n<li>In 2008, Internet Explorer dominated as the browser for 93.5% of their visitors. By 2014, that percentage had fallen to 19.7%, with Chrome bringing in the lion’s share of the traffic (37%). Firefox narrowly beat out IE with a 20% share of users.</li>\n<li>In 2008, visits from “mobile” devices accounted for only about 0.1% of their traffic. In 2014, that number had skyrocketed to 14.4%.</li>\n<li>In 2008, they detected 71 different screen resolutions, which is already a lot to consider. By 2014, however, they were seeing 1,000 unique screen resolutions each and every quarter (with over 200 of those recording 10+ visits per quarter).</li>\n</ul>\n<p>That last stat blows my mind every time I read it. You can’t design for 200 different screens, let alone 1,000. It’s a fools errand. And don’t even think of trying to test on that many devices.</p>\n<p>And yet, here we are designing websites that can (and will) go anywhere. We need to thoroughly test because we can’t make any assumptions about the browsers and devices being used to access our content.</p>\n<p>Testing can be tedious, time consuming, and costly. Surely there’s a way to make it easier. There sure is: Instead of getting hung up on creating one experience that needs to be nearly identical on every browser, we can be smarter about how we build things and treat experience as a continuum.</p>\n<p>We can build websites that are both nimble enough to work on low powered devices over slow networks <em>and</em> smart enough to take advantage of advanced features and sensors when opportunity knocks.</p>\n<p><em>Wha?! We can have our cake and eat it too</em>? Yes. Yes we can.</p>\n<h2 id=\"start-on-solid-footing\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#start-on-solid-footing\" aria-hidden=\"true\">#</a> Start on Solid Footing</h2>\n<p>When dealing with the insane proliferation of web-enabled devices and the great unknown of where our websites will go, it pays to take a step back and focus on what’s important. We need to ask ourselves two simple questions:</p>\n<ol>\n<li>What is the purpose of this page, this form, this interface?</li>\n<li>What is the simplest way to realize that purpose?</li>\n</ol>\n<p>Then we need to build that <em>first</em>. Typically we’re talking text, some basic HTML, actual links to other pages, and forms that submit to a back-end of some sort. This is our minimum viable product and it will work anywhere.</p>\n<p>Then we can look for opportunities to enhance the experience, all while keeping that functional core at the center of the experience.</p>\n<p>We can use CSS to add visual hierarchy to the page, provide some visual interest, and adjust the layout to create a good reading experience on a wide range of screen sizes. We should start from the narrowest screen size we can imagine and <a href=\"https://twitter.com/brad_frost/status/191977076000161793\">let the content guide our breakpoint decisions</a>.</p>\n<p>We’ll use JavaScript to give real-time feedback to our users. <a href=\"http://domscripting.com/presentations/xtech2006/\">We’ll hijack forms and links</a> to <a href=\"http://www.filamentgroup.com/lab/ajax-includes-modular-content.html\">lazy load additional content</a> or otherwise avoid full-page refreshes. Heck, we can even take over the entire page and <a href=\"http://nerds.airbnb.com/isomorphic-javascript-future-web-apps/\">convert it into a single page app</a>.</p>\n<p>But we should never sacrifice the functional core.</p>\n<p>This approach to designing for the web is called <a href=\"http://alistapart.com/article/understandingprogressiveenhancement\">progressive enhancement</a> and it’s the number one tool for dealing with the one-two punch of older browsers and device proliferation.</p>\n<h2 id=\"be-conservative-in-your-delivery\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#be-conservative-in-your-delivery\" aria-hidden=\"true\">#</a> Be Conservative in Your Delivery</h2>\n<p>What makes progressive enhancement so helpful when it comes to dealing with this swirling mass of devices and browsers is that the core experience will <em>always be</em> available. There is nothing precluding our users from accessing it, even on a crappy WAP browser or a text-based browser like Lynx or even some <a href=\"http://www.theubi.com/\">no so far-future talking computer</a> that only “sees” the web as text.</p>\n<p>In order to ensure we don’t accidentally deliver advanced features to less capable browsers like these, we just need to be smart about how we load stuff like CSS and JavaScript.</p>\n<p>Some basic CSS—think typography, color, etc.—will be usable by just about anyone, so we can put all that stuff in one CSS file (e.g., <code>basic.css</code>) and include it with a standard <code>link</code>. Then we can tuck all of our layout rules and other advanced CSS into a separate CSS file (e.g., <code>advanced.css</code>) that we link to with an associated media query.</p>\n<pre class=\"language-html\" tabindex=\"0\"><code class=\"language-html\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>link</span> <span class=\"token attr-name\">rel</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>stylesheet<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>default.css<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">media</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>all<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>link</span> <span class=\"token attr-name\">rel</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>stylesheet<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">href</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>advanced.css<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">media</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>only screen<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span></code></pre>\n<p>Any <a href=\"http://www.slideshare.net/bryanrieger/rethinking-the-mobile-web-by-yiibu/106\">browsers that don’t understand media queries will ignore the second CSS file entirely</a> and receive only the linear, mobile view. Easy peasy, and IE8 gets the mobile layout (a baseline level of support that is not likely to cause you any testing headaches).</p>\n<p>You can even take things a step further and use <a href=\"https://developer.mozilla.org/en-US/docs/Web/CSS/@supports\">the <code>@supports</code> block</a> within <code>advanced.css</code> to limit certain rule sets to only the browsers that support specific CSS features.</p>\n<p>Of course, CSS support issues are nothing compared to JavaScript, so sometimes it’s best not to deliver certain bits of JavaScript-based functionality to browsers that can’t handle it. This is where <a href=\"http://www.quirksmode.org/js/support.html\">feature (and object) detection</a> becomes incredibly useful:</p>\n<pre class=\"language-js\" tabindex=\"0\"><code class=\"language-js\"><span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token string\">\"querySelector\"</span> <span class=\"token keyword\">in</span> document<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// We can use querySelector!</span>\n<span class=\"token punctuation\">}</span></code></pre>\n<p>You can also use <a href=\"http://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment\">inverted conditional comments</a> to prohibit older versions of IE from getting JavaScript in the first place (which means you don’t even need to worry about debugging JavaScript there). Here’s an example that hides <code>main.js</code> from IE8 and below, but makes it available to IE9 on up and to every other non-IE browser.</p>\n<pre class=\"language-html\" tabindex=\"0\"><code class=\"language-html\"><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>!--[if</span> <span class=\"token attr-name\">gt</span> <span class=\"token attr-name\">IE</span> <span class=\"token attr-name\">8]</span><span class=\"token punctuation\">></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>!--</span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>script</span> <span class=\"token attr-name\">src</span><span class=\"token attr-value\"><span class=\"token punctuation attr-equals\">=</span><span class=\"token punctuation\">\"</span>main.js<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span><span class=\"token script\"></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>script</span><span class=\"token punctuation\">></span></span>\n<span class=\"token comment\">&lt;!--&lt;![endif]--></span></code></pre>\n<p>By being conservative in what we deliver to browsers we <a href=\"http://bradfrost.com/blog/mobile/support-vs-optimization/\">ensure the greatest level of support, but can still optimize for more advanced ones</a>. This makes testing so much easier because we know older browsers will be okay with the basics and we aren’t trying to use JavaScript features unless we know they’re available.</p>\n<h2 id=\"test%2C-test%2C-test\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#test%2C-test%2C-test\" aria-hidden=\"true\">#</a> Test, Test, Test</h2>\n<p>Progressive enhancement helps us avoid a lot of rendering and scripting issues before we even get to the testing phase of a project, but eventually we do need to sit down and run our projects through their paces.</p>\n<p>In order to keep testing manageable during development, it’s best to test in one browser we know to have good web standards support. It will provide a basic gut-check for our work. Once we are pretty confident things are working as they should be, we can begin more thorough testing on a variety of browsers and devices.</p>\n<p>I personally keep several versions of each major browser on my laptop at any given time. I work on a Mac, so I typically have a few versions of Chrome, Firefox, and Opera lying around. You can get older versions of these browsers here:</p>\n<ul>\n<li><a href=\"http://commondatastorage.googleapis.com/chromium-browser-continuous/index.html\">Chromium Archive</a></li>\n<li><a href=\"http://ftp.mozilla.org/pub/mozilla.org/firefox/releases/\">Firefox Archive</a></li>\n<li><a href=\"http://www.opera.com/download//?custom=yes\">Opera Archive</a></li>\n</ul>\n<p>It’s near-impossible to get older versions of Safari running on modern versions of OS X, so I typically just have the latest version locally.</p>\n<p>For testing on Windows versions of said browsers, I typically have anywhere from 3-5 virtual machines running various Windows versions with their associated browser version and (typically) a copy of Chrome, Firefox, and Opera for good measure. The MS Edge Dev site offers <a href=\"https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/\">free Windows VMs for download</a>. If you’re just looking to get a gut-check in the latest and greatest from Microsoft, there’s also <a href=\"https://remote.modern.ie/\">the Remote.IE service</a>, which allows you to connect to a virtualized version of the browser.</p>\n<p>If you develop on Windows or Linux, you’ll need access to a Mac or you’ll have to rely on virtualization to test on that platform. I’ll discuss virtualization in a moment.</p>\n<p>Once you’ve thoroughly tested in the various desktop browsers, it’s time to take the deep dive into the world of devices. If you’re unsure where to begin, take a look at your analytics, but take them with a grain of salt. Analytics can lead you to make false assumptions. For example, if you see a low percentage of Blackberry users, could that be because your site just doesn’t work well in Blackberry so they don’t stick around (or come back)? Beware the self-fulfilling prophecy.</p>\n<p>If you have the budget, by all means pick up some devices to have on hand for testing. You can use tools like <a href=\"https://creative.adobe.com/products/inspect\">Adobe’s Edge Inspect</a>, <a href=\"http://vanamco.com/ghostlab/\">Vanamco’s Ghostlab</a>, or <a href=\"https://github.com/viljamis/Remote-Preview\">Viljami Salminen’s Remote Preview</a> (or a combination of all of the above) to synchronize browsing on a handful of devices. Some of these tools also allow for remote inspection of the device to debug CSS and JavaScript. <a href=\"http://people.apache.org/~pmuellr/weinre-docs/latest/\">Weinre</a> (which Adobe Edge Inspect uses) and <a href=\"http://vorlonjs.com/\">Vorlon.js</a> also provide remote inspection functionality.</p>\n<p>If you are lucky enough to have one nearby, you should stop by your local <a href=\"http://opendevicelab.com/\">open device lab (ODL)</a> to run your tests. ODLs are free community resources, typically offered by a web design studio or an individual who happens to be sitting on a ton of devices. Someone at the ODL should be able to help you pick out devices to test on and introduce you to the testing tools they have available in the lab.</p>\n<p>If you don’t have an ODL nearby, you can also do some guerrilla-style testing in your local mobile phone or electronics store. Just make sure they have real devices… you won’t get far on the fake plastic ones.</p>\n<p>If none of these are options for you, there’s always virtualization. You can download and install <a href=\"http://www.mobilexweb.com/emulators\">emulators for a variety of mobile browsers and devices</a>. Additionally, services like <a href=\"https://www.browserstack.com/\">Browserstack</a> and <a href=\"http://crossbrowsertesting.com/\">CrossBrowserTesting</a> offer access to hundreds of virtual desktops and devices for a nominal fee.</p>\n<p>Virtualization will never give you the same experience as holding a real device in your hand. The performance is rarely the same and you don’t get any sort of feel for how the device responds to your input. I once stumbled on an Android 2.3 bug wherein generated content was being re-generated and re-inserted every time the device was rotated. I doubt I would have discovered that using an emulator. That said, emulators can help you get a rough idea of whether your interface works or not.</p>\n<p>Regardless of the means by which you procure your testing devices, try to cover a good cross-section. Pick some low-end ones, a couple older high-end devices, and handful of the latest flagships, and a wide variety of screen sizes and resolutions. Make sure you have good coverage in terms of operating systems too—the latest iOS and Android versions are a given, but make sure you have a Windows device or two, a few Blackberry options, and some older Android and iOS versions in the mix. Then throw in <a href=\"http://www.geekwire.com/2013/microsoft-kin-resurfaces-25-daily-deals-site/\">an oddball</a> <a href=\"http://www.windowscentral.com/have-39-spare-then-pick-odd-sylvania-7-netbook-windows-ce\">or two</a> to see if your interfaces hold up.</p>\n<p>It’s important to bear in mind that we will never be able to give each user on each device exactly the same experience. We need to be okay with that—experience is a continuum. As long as our users can accomplish what they need to on our sites, they will be well-served.</p>\n<h2 id=\"bonus-points%3A-embrace-patterns\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#bonus-points%3A-embrace-patterns\" aria-hidden=\"true\">#</a> Bonus Points: Embrace Patterns</h2>\n<p>If we really want to make things easy on ourselves (and our team), we should consider building a <a href=\"http://alistapart.com/blog/post/getting-started-with-pattern-libraries\">pattern library</a> before we build a single page of our website.</p>\n<p>Breaking our interfaces down into discrete, repeatable patterns (e.g., a label and form control, a tabbed interface, etc.) lets us look at each in isolation and test it that way too. Testing in isolation is far easier than trying to debug a page with a lot of moving parts.</p>\n<p>Gathering our patterns into a live, web-based pattern library allows anyone on our team to collect the patterns they need to build a given interface as easily as they’d assemble a plate at a smörgåsbord. And if we really want to streamline the building and testing process, we can even <a href=\"http://ianfeather.co.uk/a-maintainable-style-guide/\">make the patterns importable into the live site</a> so everything stays in sync.</p>\n<h2 id=\"don%E2%80%99t-fear-the-zombie-apocalypse\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#don%E2%80%99t-fear-the-zombie-apocalypse\" aria-hidden=\"true\">#</a> Don’t Fear the Zombie Apocalypse</h2>\n<p>With the constant onslaught of new devices, form factors, and considerations, it’s tempting to throw up our hands and find another line of work. The pace of advancement is so brisk, it’s just hard to keep up, let alone feel like we’re on top of where things are headed.</p>\n<p>Thankfully, however, all is not lost. By taking a step back and focusing on what matters, embracing experience as a continuum, and being deliberate in how (and when) we deliver certain features and functionality to browsers, we’ll head most issues off at the pass.</p>\n<p>This relieves some of the pressure (and frustration) from the testing process and frees us up to test on a wider variety of devices and browsers, which means we’ll be able to provide a solid experience for more users, no matter what marvel of technological wizardry they happen to be using at the time.</p>\n<p>Everybody wins.</p>\n","url":"https://www.aaron-gustafson.com/notebook/bringing-sanity-and-order-to-device-testing/","tags":["web design","mobile","devices","progressive enhancement","web development"],"date_published":"2015-06-05T13:56:03Z"},{"id":"https://www.aaron-gustafson.com/notebook/tips-for-surviving-googles-mobilegeddon/","title":"✍🏻 Tips for Surviving Google’s “Mobilegeddon”","summary":"Today is the day Google updates it algorithm to take into account mobile-friendliness. Here are a few tips that will help you embrace mobile without tearing your hair out.","content_html":"<p>Today is the day <a href=\"http://www.economist.com/news/business-and-finance/21648947-worlds-biggest-search-engine-shakes-up-its-algorithms-mobilegeddon\">Google updates it algorithm to take into account mobile-friendliness</a>. Here are a few tips that will help you embrace mobile without tearing your hair out.</p>\n<h2 id=\"1.-embrace-mobile-first-css\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#1.-embrace-mobile-first-css\" aria-hidden=\"true\">#</a> 1. Embrace mobile-first CSS</h2>\n<p>This is a quick win. I’ve done the mobile-first overhaul on a number of existing sites over the last few years and the best strategy I’ve found is this:</p>\n<ol>\n<li>Create two CSS files. Name the first something like “basic.css” and link to it with <code>media=&quot;all&quot;</code>. Name the second something like “advanced.css” and link to it with <code>media=&quot;only screen&quot;</code>.</li>\n<li>Move all of your existing desktop-only CSS into advanced.css and wrap the rules in a media query corresponding to your design width (e.g. <code>@media (min-width:60em)</code> for a 960px max width).</li>\n<li>Open a page from your site in a new browser window and make it as small as possible. Move any basic typographic and color styles from advanced.css to default.css and refresh the page. How does it look? Make the browser wider. Still acceptable? Awesome! This is the CSS you’ll serve to older browsers that don’t understand media queries and it forms the basis of your larger screen styles.</li>\n<li>Shrink the browser back down to the smallest it can go and create a breakpoint for that size in advanced.css (e.g. <code>@media (min-width:10.5em)</code> if you want to small target devices like the <a href=\"https://getpebble.com/\">Pebble</a>). Move any styles you want applied in this scenario from your “full screen” breakpoint and add any new rules you need to make things look good.</li>\n<li>Increase the browser width until the layout looks odd, then make a new breakpoint and move in or add the necessary styles.</li>\n<li>Rinse &amp; repeat until you get up to the “full screen” layout.</li>\n<li>Move your print styles—you do have print styles, right?—into default.css and wrap them in a print <code>@media</code> block.</li>\n</ol>\n<p>For another approach to responsive retrofitting, check out <a href=\"http://webstandardssherpa.com/reviews/responsive-retrofitting/\">this piece from Ben Callahan</a>.</p>\n<h2 id=\"2.-focus-on-key-tasks\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#2.-focus-on-key-tasks\" aria-hidden=\"true\">#</a> 2. Focus on key tasks</h2>\n<p>Every page has a purpose. If it doesn’t, you don’t need it. Find the page’s purpose and make that the focus. Eliminate distractions and reduce the amount of friction a user encounters when trying to accomplish the task.</p>\n<p>If company or client politics preclude you from getting rid of all distractions on a page, consider <a href=\"http://www.filamentgroup.com/lab/ajax-includes-modular-content.html\">a lazy-loading strategy</a> to bring them in with JavaScript only when there is more screen real estate.</p>\n<h2 id=\"3.-get-smarter-about-images\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#3.-get-smarter-about-images\" aria-hidden=\"true\">#</a> 3. Get smarter about images</h2>\n<p>Images are great, but they often comprise the majority of a web page’s bulk. To slim things down, first consider whether a given image is actually needed. If it isn’t, get rid of it. If it offers an enhancement, but isn’t crucial, consider lazy-loading it after page load. If the image is really important, <a href=\"https://ericportis.com/posts/2014/srcset-sizes/\">use <code>srcset</code> and <code>sizes</code></a> (or the <code>picture</code> element) to deliver the smallest and yet most appropriate image to your users, based on their device.</p>\n<h2 id=\"4.-embrace-the-continuum\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#4.-embrace-the-continuum\" aria-hidden=\"true\">#</a> 4. Embrace the continuum</h2>\n<p>As designers and developers, we often try to control everything. But we have to realize that we can’t control everything on the Web. People on different devices have different capabilities and considerations. They will need your website to be flexible enough to allow them to accomplish their goals no matter what.</p>\n<p>Understanding that is key to building experiences that move seamlessly across devices. You know, <a href=\"http://alistapart.com/article/understandingprogressiveenhancement\">progressive enhancement</a>. It’s key to embracing the Web for all of its inherent web-iness.</p>\n<hr>\n<p>And there you have it: 4 simple, but effective guidelines for creating awesome websites that will fair well under Google’s new algorithm. And guess what: Your users will benefit too.</p>\n","url":"https://www.aaron-gustafson.com/notebook/tips-for-surviving-googles-mobilegeddon/","tags":["web design","mobile","progressive enhancement"],"date_published":"2015-04-21T13:10:12Z"},{"id":"https://www.aaron-gustafson.com/speaking-engagements/adaptive-designs-across-devices/","title":"📢 Adaptive Designs Across Devices","summary":"Progressive Enhancement, the heart of Adaptive Design, makes the life of a design less complicated. Aaron and Jenn will show you how to create a solid core and build out to craft amazing user experiences that work regardless of devices capabilities or deficiencies.","content_html":"<p>Progressive Enhancement, the heart of Adaptive Design, makes the life of a design less complicated. Aaron and Jenn will show you how to create a solid core and build out to craft amazing user experiences that work regardless of devices capabilities or deficiencies.</p>\n<p>Practical takeaways from this workshop:</p>\n<ul>\n<li>Integrate progressive enhancement with your process to take advantage of features and components as they surface.</li>\n<li>Prevent existing design decisions that unintentionally skew your analytics and narrow your outlook on web and mobile experiences.</li>\n<li>Fuse responsive web design and mobile­-first with progressive enhancement, and envision content, core tasks, and visual designs on a continuum.</li>\n</ul>\n","social_text":"Progressive Enhancement, the heart of Adaptive Design, makes the life of a design less complicated. Aaron and Jenn will show you how to create a solid core and build out to craft amazing user experiences that work regardless of devices capabilities or deficiencies.","url":"https://www.aaron-gustafson.com/speaking-engagements/adaptive-designs-across-devices/","tags":["progressive enhancement","mobile","user experience"],"image":"https://www.aaron-gustafson.com/undefined","date_published":"2015-04-13T13:00:00Z"},{"id":"https://www.aaron-gustafson.com/appearances/podcasts/2015-01-26-cross-device-adaptive-design/","title":"🎧 Cross-device Adaptive Design","content_html":"<p>Jenn Lukas and I talked to Jared about responsive web design, progressive enhancement, and mobile in advance of our talk and workshop at UXIM in April.</p>\n","url":"http://archive.uie.com/brainsparks/2015/01/26/aaron-gustafson-jenn-lukas-cross-device-adaptive-design/","tags":["responsive web design","progressive enhancement","mobile"],"date_published":"2015-01-26T00:00:00Z"},{"id":"https://www.aaron-gustafson.com/notebook/an-onslaught-of-low-cost-smartphones/","title":"✍🏻 An Onslaught of Low-cost Smartphones","summary":"It seems every week we are seeing more and more low-cost devices being launched in an effort to connect the unconnected. Here’s why that maters to you.","content_html":"<p>It seems every week we are seeing more and more low-cost devices being launched in an effort to connect the unconnected. For instance, this past week saw the announcement of <a href=\"http://www.theverge.com/2015/1/14/7544107/microsoft-lumia-435-532-launch-specs-price-release-date\">two new entries in the Microsoft Lumia line, the 435 and the 532</a>, and the announcement of <a href=\"http://arstechnica.com/gadgets/2015/01/samsung-finally-puts-tizen-on-a-smartphone-launches-the-z1-in-india/\">Samsung’s first Tizen phone, the Z1</a>.</p>\n<p>The two Lumia devices are the cheapest Windows Phone offerings to date: The Lumia 435 is €69 (about US$80) and the Lumia 532 is just slightly more expensive at €79 (about US$94). By comparison, the Lumia 535 was the previous low-cost Windows Phone winner at US$135. And Samsung’s Z1, which is aimed at the Indian market, runs a mere Rs 5,700 (roughly US$92).</p>\n<p>Now if you’ve read this far, you are probably wondering why this matters. Well, spec-wise, many of these lower-end devices are far from what we are used to. All of these devices are running a resolution of 800×480. And their processing speeds are slower than the device you likely have in your pocket. But they are all far cheaper too. And remember that people in China and India have <a href=\"#fig-2015-01-15-01\">far less disposible income than we do</a>.</p>\n<figure id=\"fig-2015-01-15-01\" class=\"media-container\">\n<p><img src=\"https://www.aaron-gustafson.com/i/posts/2014-11-06/05-lg.jpg\" alt=\"A bar chart comparing the average monthly incomes of people living in the U.S., Canada, China, and India.\"></p>\n</figure>\n<p>If you only earned US$295 a month, a US$799 smartphone would likely be out of the question.</p>\n<p>As Web designers and developers, we need to expose ourselves to the Web as others see it. This is why we need to view our sites and applications on lower-end devices and over lower-speed—2G, 3G, etc.—connections. Only by testing our wares can we truly ensure our content and services are available and accessible by anyone, anywhere, on any device. You know, <a href=\"http://webfoundation.org/about/vision/\">the vision for the Web</a>.</p>\n<p>Of course you probably don’t have the budget to purchase a bunch of devices, no matter how cheap they are. So how do you test on them? <a href=\"http://opendevicelab.com/\">Visit a local Open Device Lab</a> like <a href=\"http://chadevicelab.org\">the one we have here in Chattanooga</a>. Or if you don’t have one near you, consider getting together with some colleagues and <a href=\"http://lab-up.org/\">starting an ODL in your area</a>.</p>\n","url":"https://www.aaron-gustafson.com/notebook/an-onslaught-of-low-cost-smartphones/","tags":["mobile","web design","devices"],"date_published":"2015-01-15T16:58:11Z"},{"id":"https://www.aaron-gustafson.com/notebook/webos-is-back/","title":"✍🏻 webOS is Back","summary":"It may have been the laughing stock in the app development community, but I’ve always had a soft spot in my heart for webOS. And now it’s back!","content_html":"<p>You may not remember it, but Palm’s groundbreaking—yes, I said groundbreaking—operating system, <a href=\"https://en.wikipedia.org/wiki/WebOS\">webOS</a>, has been resuscitated yet again. This time by LG.</p>\n<p>For those of you who may have missed out on webOS in the past, here’s a primer: webOS was developed by Palm and debuted on the Palm Pre in 2009. It was the first “HTML5-based” operating system. Yes you read that right: the OS was built on HTML, CSS, and JavaScript. In 2009. Six. Years. Ago.</p>\n<p>Applications for webOS were written using the front-end Web stack, with the JavaScript application logic tied into the device via the Mojo—and later Enyo—JavaScript application framework. Though novel at the time, this concept has since taken off in the intervening years. <a href=\"http://msdn.microsoft.com/en-us/library/windows/apps/dn631758.aspx\">Windows</a>, <a href=\"https://marketplace.firefox.com/developers/\">Firefox OS</a>, and <a href=\"https://www.google.com/search?q=build%20native%20apps%20with%20html5\">countless “unified” app development platforms</a> have embraced the front-end Web stack as a means of building software.</p>\n<p>webOS also supported multitasking, which was not common back then. And to make it easy to manage, webOS introduced the brilliant card metaphor for viewing and switching between running applications. Apple, of course, ripped that idea off for iOS 7. If you dig that feature on your iPhone you have Palm to thank.</p>\n<p>Sadly, Palm usage was on the decline when webOS debuted so operating system never really took off. Eventually Palm was engulfed by HP, which launched the ill-fated HP TouchPad. It too failed to gather any steam and we thought webOS was dead (despite many proclamations to the contrary from well-meaning folks at HP).</p>\n<p>In 2013 <a href=\"http://www.theverge.com/2013/2/25/4027814/hp-emerges-as-big-winner-in-webos-sale\">HP announced it was selling parts of webOS to LG</a> for use in smart TVs. It was a glimmer of hope for the beleaguered OS. And now, not only do we have webOS based <a href=\"http://www.lg.com/uk/smarttv/webos\">smart TVs</a>, but <a href=\"http://gizmodo.com/lgs-new-smart-watch-is-powered-by-webos-1678191522\">LG’s new smart watches are sporting the OS as well</a>. And LG has not ruled out bringing it back for a phone either.</p>\n<p>I’m happy to see webOS sticking around. It may have been the laughing stock in the app development community back in the day, but I’ve always had a soft spot in my heart for it.</p>\n","url":"https://www.aaron-gustafson.com/notebook/webos-is-back/","tags":["mobile","devices","web design"],"date_published":"2015-01-08T15:17:09Z"},{"id":"https://www.aaron-gustafson.com/publications/articles/reflecting-on-the-revamped-sherpa/","title":"📄 Reflecting on the Revamped Sherpa","content_html":"<p>I turn the spotlight on the Sherpa site and walks through some of the choices we made for the mobile-first overhaul of this site.</p>\n","url":"http://webstandardssherpa.com/reviews/reflecting-on-the-revamped-sherpa/","tags":["mobile","web design"],"date_published":"2014-10-28T00:00:00Z"},{"id":"https://www.aaron-gustafson.com/notebook/native-vs-stylable-tug-of-war/","title":"✍🏻 The “Native” vs. “Stylable” Tug of War","summary":"In his astute post “‘Native experience’ vs styling select boxes”, Bruce Lawson correctly identified a common tension in the web world: wanting better form controls vs. wanting to throw away what the browser does. Here are my thoughts.","content_html":"<p>In his astute post <a href=\"https://www.brucelawson.co.uk/2014/native-experience-vs-styling-select-boxes/\">“‘Native experience’ vs styling select boxes”</a>, Bruce Lawson correctly identified a common tension in the web world:</p>\n<blockquote>\n<p>But why this urge to re-style page elements that end-users are familiar with? … Or is it that we love native look and feel, except when we don’t?</p>\n</blockquote>\n<p>Speaking as the guy who not only wrote JavaScript to help me create an accessible <code>select</code> element alternative, but who also made it <a href=\"https://books.google.com/books/content?id=Wmg6dkBJd50C&amp;pg=PA507&amp;img=1&amp;zoom=3&amp;hl=en&amp;bul=1&amp;sig=ACfU3U2DB7JkEBRWEjSwZLHwIJnpGqgq9w&amp;w=1280\">the focus of a case study (PNG, 128kB)</a> in <a href=\"https://amzn.to/TaoffD\">AdvancED DOM Scripting</a>, I am fully aware of the desire to have it both ways. I have not often seen the desire for both in a single individual, but it does happen in one particular instance occasionally.</p>\n<p>Based on my own experience, I see the following arguments in favor of changing the display of a browser-delivered UI control quite often:</p>\n<ol>\n<li>It doesn’t look good to me.</li>\n<li>It is not “on brand”.</li>\n<li>It clashes with our brand’s color scheme.</li>\n<li>We want the web experience to feel like a platform-specific app.</li>\n<li>It doesn’t behave how we think it should.</li>\n</ol>\n<p><em>(<abbr lang=\"it\" title=\"nota bene: please note\">n.b.</abbr> Browsers have done a pretty good job reducing the amount of color and the overall visual strength used in their UI controls to help them better blend in with a wide variety of designs, so clashes as mentioned in #3 happen far less often than they did nearly a decade ago.)</em></p>\n<p>As the weathered, battle tested (and, admittedly, somewhat jaded) front-end dev that I am, I typically push back with one or more of the following:</p>\n<h2 id=\"in-addressing-desired-design-changes\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#in-addressing-desired-design-changes\" aria-hidden=\"true\">#</a> In Addressing Desired Design Changes</h2>\n<p>In terms of aesthetics (addressing arguments 1, 2, and 3), I understand where you’re coming from. Browser-delivered UI controls are not the most appealing things, but they are familiar to your users. A <code>select</code> box they see on your site that looks like the one they see on Wikipedia or their banking site will be immediately recognizable. Sure, the looks and feel may differ from browser to browser, but most people use only a small number of browsers throughout the day—at work, at home, on their device—and if you want to ensure the design of a form control feels “right” in the browser they are using, sometimes it’s best to let go of that control.</p>\n<h2 id=\"in-addressing-os-parity\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#in-addressing-os-parity\" aria-hidden=\"true\">#</a> In Addressing OS Parity</h2>\n<p>I can understand the desire to have a form control in a web page look and feel like the same (or a similar) control within the operating system (argument 4), but I am not sure that’s a rabbit hole you want to go down. Here’s why: Achieving exact design and functional parity between a platform-specific control and a web control quite often requires extra markup, a bunch of CSS, and a bit of JavaScript. Anything is achievable with unlimited time and budget, so it’s completely doable, but it would be good to estimate the cost to see if you still think it is a worthwhile endeavor.</p>\n<p>Assuming it is, we then have the question of which operating system to model the control after. Or maybe you want to offer a different take on the control based on the operating system your user is using. In that case, we may need to multiply the original estimate by the number of operating systems you want to support. But it’s worth noting that, in the Android world, different device manufacturers often “skin” the operating system to look different from other ones. Sometimes they even do it on a device-by-device basis. We’ll need to figure out which ones you want to include in your platform-like control matrix and multiply the estimate accordingly.</p>\n<p>Then there’s maintenance. We’ll need to test these platform-like controls on each of their corresponding platforms and test the script that determines which experience gets delivered to which device to make sure we’re not accidentally sending the wrong experience. We’ll also need to test the delivery script on every other browser in our test matrix to ensure it is not causing issues there.</p>\n<p>What should we do when a new operating system version is rolled out? iOS, for example, has made radical shifts in the design of their platform-specific controls in each major release. We’ll probably want to create unique versions of the control for each version of each OS we support and we’ll need to keep tabs on upgrades so we don’t end up confusing our users if they visit our site in iOS 7 and have a control that looks like it’s from iOS 6. We’ll need to add the number of OS versions into the multiplier as well.</p>\n<p>Ok, and finally: How many controls did you want to apply this approach to again?</p>\n<p>Or we could use the browser-delivered form control and it will just work.</p>\n<h2 id=\"in-addressing-altered-behavior\" tabindex=\"-1\"><a class=\"header-anchor\" href=\"#in-addressing-altered-behavior\" aria-hidden=\"true\">#</a> In Addressing Altered Behavior</h2>\n<p>I completely agree that not all browser-delivered UI controls work exactly how I would like, but there are several risks in changing the expected behavior.</p>\n<p>First of all, there’s the possibility we could actually end up making the interface more confusing or that the change in behavior might not be exactly what our user’s wanted (either based on what they are used to or our mental model not aligning with theirs). In order to rule out these issues, we should run a few rounds of usability tests. These could be quick café tests or more formal studies depending on the budget.</p>\n<p>Assuming our tests go well, we will need to maintain this code and do all of the requisite browser testing. And potentially upgrade our code as new browsers and browser versions come out. Depending on the complexity of the code, this could become a large requirement, but if it is ultimately in the service of making the web a better, more usable interaction environment, it could be worth it.</p>\n<p>For what it’s worth, if we go this route and are successful, we should consider getting involved in the spec-writing process at the <a href=\"https://w3.org\">W3C</a> or <a href=\"https://whatwg.org\">WhatWG</a>. We should contribute our recommended changes back to the community and share what we learned. If we make a compelling argument, perhaps our idea will become part of some future standard and we can taper off our browser testing when the change lands in the platform.</p>\n<hr>\n<p>As you can probably tell, I’m not a really big fan of changing existing controls as I feel it can amount to a wasted effort. That said, if there are design improvements to be made—“design” in the true sense: being about how usable something is, not just how aesthetically-pleasing it is to someone (e.g. improving contrast, making the control more intuitive, etc.)—I’m willing to accept the change as something we <em>should</em> do and then work to make sure that change has been vetted and, if successful, given away for inclusion in other projects. If it solves a major issue on the web, I want to give that change every opportunity to make it into the appropriate spec by talking to the appropriate folks about it both in-person, in blog posts, and on the appropriate mailing list. If the change solves a problem in a specific browser, I want to see it incorporated into said browser and will file a bug report and try to build momentum around it by engaging the community.</p>\n<p>Anyway, that’s my general position on augmenting browser-delivered UI controls. What are your thoughts on the topic?</p>\n<p><em>Note: I no longer use “native” in this context, but it remains in quoted material.</em></p>\n","url":"https://www.aaron-gustafson.com/notebook/native-vs-stylable-tug-of-war/","tags":["web design","browsers","design","mobile"],"date_published":"2014-07-17T12:21:47Z"},{"id":"https://www.aaron-gustafson.com/notebook/searching-for-the-right-size/","title":"✍🏻 Searching for the “Right Size”","content_html":"<p>This <a href=\"https://www.wired.com/2014/07/what-a-stalling-tablet-market-says-about-our-search-for-the-perfect-screen/\">recent piece from <cite>Wired</cite></a> attributes dwindling tablet sales to cannibalization from larger mobile phones (<abbr title=\"also known as\">aka</abbr> “phablets”) which are nearly as big as 7-8˝ tablets:</p>\n<blockquote>\n<p>Aside from the ability to make a phone call, the difference between a phone and a tablet comes down to 1.5 inches or less. … But the real issue is device makers are running out of good arguments for why these ever more subtle size gradations matter. After a point, the differences come down to personal preference rather than any meaningful new use case. As phones and tablets converge into this tight window, slightly bigger phones could accelerate the decline in tablet demand.</p>\n</blockquote>\n<p>Personally, I’m not sure it matters. We’re in the midst of one big experiment being run by the device manufacturers. We’re in the scattershot. The industry is feeling out the “right” screen size (or sizes) that most people want to use and we are (in large part) footing the bill.</p>\n","url":"https://www.aaron-gustafson.com/notebook/searching-for-the-right-size/","tags":["mobile"],"date_published":"2014-07-11T00:38:16Z"},{"id":"https://www.aaron-gustafson.com/speaking-engagements/designing-across-devices-with-progressive-enhancement/","title":"📢 Designing Across Devices with Progressive Enhancement","summary":"Today, browsers are just one of the issues among a sea of considerations like accessibility, device compatibility, and responsive or adaptive design. And with new techniques and devices coming out daily, it’s easy to feel overwhelmed.","content_html":"<p>Remember the good old days when getting IE6 to “work” was our main worry?</p>\n<p>My, we’ve come a long way, baby.</p>\n<p>Today, browsers are just one of the issues among a sea of considerations like accessibility, device compatibility, and responsive or adaptive design. And with new techniques and devices coming out daily, it’s easy to feel overwhelmed.</p>\n<p>Fortunately, I know how to wrangle all of these elements using progressive enhancement. With his practical approach, he designs for humans on any spectrum ­­ with and without javascript enabled—and soon enough, you will, too.</p>\n<p>Create experiences without technological constraints</p>\n<ul>\n<li>Understand progressive enhancement and integrate it with your process</li>\n<li>Use content as your foundation and build the experience from there</li>\n</ul>\n<p>Stop assuming users are just like you</p>\n<ul>\n<li>Come to a better understanding of how existing design decisions can unintentionally skew your analytics</li>\n<li>Prevent the typical myopic view of web and mobile experiences</li>\n</ul>\n<p>Make a content­-first approach work</p>\n<ul>\n<li>Fuse responsive web design and mobile­-first with progressive enhancement</li>\n<li>Focus content, core tasks, and visual designs on a continuum</li>\n</ul>\n<p>Establish a solid strategy for planning</p>\n<ul>\n<li>Sketch different experiences using basic UI elements and flow charts</li>\n<li>Plan decision points and outcomes from simple through complex interactions</li>\n</ul>\n<p>Join us for this seminar if you:</p>\n<ul>\n<li>Need a manageable design process that works for your whole team</li>\n<li>Feel your sanity slipping away as more devices launch every. single. day.</li>\n<li>Want to plan, design, and test all potential experiences across platforms</li>\n</ul>\n<p>If you’re trying to create a better web ­­ and are open to rethinking how you approach designing for any interface, then you need to watch Aaron’s seminar.</p>\n","social_text":"Today, browsers are just one of the issues among a sea of considerations like accessibility, device compatibility, and responsive or adaptive design. And with new techniques and devices coming out daily, it’s easy to feel overwhelmed.","url":"https://www.aaron-gustafson.com/speaking-engagements/designing-across-devices-with-progressive-enhancement/","tags":["accessibility","inclusive design","mobile","progressive enhancement","responsive web design"],"image":"https://www.aaron-gustafson.com/undefined","date_published":"2013-11-21T17:30:00Z"},{"id":"https://www.aaron-gustafson.com/publications/articles/qanda-apple-products-and-mobile-assumptions/","title":"📄 Q&amp;A: Apple Products and Mobile Assumptions","content_html":"<p>I think there is a tendency in our industry to focus on the latest and greatest technology, which many would say includes Apple products.</p>\n","url":"http://webstandardssherpa.com/ask-the-sherpas/apple-products-and-mobile-assumptions","tags":["mobile","industry","web design","web development"],"date_published":"2013-01-10T00:00:00Z"},{"id":"https://www.aaron-gustafson.com/notebook/slides-from-my-talk-at-how-interactive/","title":"✍🏻 Slides from my talk at HOW Interactive","summary":"These last two days have been a bit of a whirlwind, but I have had a great time meeting and talking to the attendees (and other speakers) here at the HOW Interactive conference in San Francisco . I gave my talk yesterday on progressive…","content_html":"<p>These last two days have been a bit of a whirlwind, but I have had a great time meeting and talking to the attendees (and other speakers) here at the <a href=\"http://www.howinteractiveconference.com/ehome/35392/\">HOW Interactive conference in San Francisco</a>. I gave <a href=\"http://www.howinteractiveconference.com/ereg/popups/sessiondetails.php?eventid=35392&amp;sessionid=2198869&amp;sessionchoice=2\">my talk</a> yesterday on progressive enhancement (of course) and how it can make designing and devloping for mobile a little more sane. Here are the slides (which you can also <a href=\"http://www.slideshare.net/AaronGustafson/progressive-enhancement-mobile\">see and download on Slideshare</a>) form that talk:</p>\n<figure><iframe frameborder=\"0\" height=\"426\" src=\"http://www.slideshare.net/slideshow/embed_code/14940120\" width=\"510\"> </iframe></figure>\n<p>I also provided attendees with <a href=\"http://readlists.com/7d414b24/\">a reading list</a>. It’s the one I <a href=\"http://adactio.com/journal/5813/\">developed for a private training a few weeks ago with Jeremy</a> and one which I will continue to update as I find more useful resources I want to share.</p>\n","url":"https://www.aaron-gustafson.com/notebook/slides-from-my-talk-at-how-interactive/","tags":["progressive enhancement","presentations","mobile"],"date_published":"2012-10-30T21:57:27Z"},{"id":"https://www.aaron-gustafson.com/publications/articles/master-mobile-navigation/","title":"📄 Master Mobile Navigation","content_html":"<p>Users want content. But first you need to ensure they can locate it, whatever their device type. In this article, I discuss top strategies to adapt your navigation to smaller screens.</p>\n","url":"http://www.creativebloq.com/mobile/master-mobile-navigation-2135807","tags":["mobile","user experience","progressive enhancement","HTML","CSS","responsive web design"],"date_published":"2012-09-15T00:00:00Z"}]}