<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Joey Hoer's Blog</title>
 <link href="https://joeyhoer.com/atom.xml" rel="self"/>
 <link href="https://joeyhoer.com/"/>
 <updated>2019-10-15T17:22:24-04:00</updated>
 <id>https://joeyhoer.com</id>
 <author>
   <name>Joey Hoer</name>
   <email></email>
 </author>

 
 <entry>
   <title>Creating Medium-style Permalinks in Jekyll</title>
   <link href="https://joeyhoer.com/creating-medium-style-permalinks-in-jekyll-5aeb11d0"/>
   <updated>2019-08-13T11:10:44-04:00</updated>
   <id>https://joeyhoer.com/creating-medium-style-permalinks-in-jekyll</id>
   <content type="html">&lt;p&gt;As I’ve &lt;a href=&quot;https://joeyhoer.com/the-url-is-dead-long-live-the-url-8e87de87#naming-urls&quot;&gt;previously written&lt;/a&gt;, information that is subject to change has no place in a URL. This is because a URL is an agreement to serve a specific piece of content from a predictable location for as long as possible. As Tim Berners-Lee wrote, “&lt;a href=&quot;https://www.w3.org/Provider/Style/URI.html&quot;&gt;cool URIs don’t change&lt;/a&gt;.” With this in mind, designing a human-friendly bulletproof URL structure can be challenging.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Tim Berners-Lee recommends that webmasters avoid using the subject (i.e. post title or topic) in the URL. Despite the fact that using the subject often is human readable, the title has the potential to change over time. For instance, we know that keywords within headers and URLs impact search rankings and impact a user’s perception of credibility. For this reason, and whenever content changes, content authors and Search Engine Optimizers may want to experiment with title and keyword changes to improve search rankings.&lt;/p&gt;

&lt;p&gt;We want a URL structure that is human readable, provides the flexibility to be changed periodically, but continues to serve the same content even if the URL changes.&lt;/p&gt;

&lt;p&gt;Fortunately, we already have an example of a website that achieves just that—&lt;a href=&quot;https://medium.com/&quot;&gt;Medium&lt;/a&gt;. Medium assigns each post an ID which is appended to the post title.&lt;/p&gt;

&lt;dl class=&quot;compact ul&quot;&gt;
  &lt;dt&gt;Medium Post ID &lt;code class=&quot;highlighter-rouge&quot;&gt;adbec59c7cf4&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;ul&gt;
      &lt;li&gt;Minimal URL: &lt;a href=&quot;https://medium.com/@torgo/adbec59c7cf4&quot;&gt;https://medium.com/@torgo/adbec59c7cf4&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Augmented URL: &lt;a href=&quot;https://medium.com/@torgo/another-title-adbec59c7cf4&quot;&gt;https://medium.com/@torgo/another-title-adbec59c7cf4&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Full URL: &lt;a href=&quot;https://medium.com/@torgo/in-defense-of-the-url-adbec59c7cf4&quot;&gt;https://medium.com/@torgo/in-defense-of-the-url-adbec59c7cf4&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;As can be seen here, the “title” portion of the URL is inconsequential—only the post ID matters. By appending the ID to the end of the URL, Medium ensures that keywords are closer to the root domain, and are less likely to be truncated within search results or elsewhere. I find this approach particularly elegant, so I attempted to recreate this URL structure within Jekyll.&lt;/p&gt;

&lt;p&gt;The first step was to create a static ID that could be used within the URL. Because Jekyll has no persistent database, almost every value within Jekyll is subject to change. My goal here was to select a post attribute that would (&lt;em&gt;almost&lt;/em&gt;) never change to use as the static ID. The attribute that I deemed least likely to change was the post &lt;code class=&quot;highlighter-rouge&quot;&gt;date&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The creation date of the document—the date the URI is created—is one thing which will not change. … That is one thing with which it is good to start a URI. If a document is in any way dated, even though it will be of interest for generations, then the date is a good starter.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://www.w3.org/Provider/Style/URI.html&quot;&gt;Cool URIs don’t change&lt;/a&gt;, Tim Berners-Lee&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The only time I could see the date becoming an issue is if I ever decided to change a post date, or if I ever published two posts with the exact same post date. Both scenarios are unlikely, but if either situation ever does occur it will require special consideration.&lt;/p&gt;

&lt;p&gt;Once I had decided to use &lt;code class=&quot;highlighter-rouge&quot;&gt;date&lt;/code&gt; as the static ID, I had to decide how I wanted to display that date within the URL. I could have used any combination of&lt;code class=&quot;highlighter-rouge&quot;&gt;year-month-day&lt;/code&gt;, but the less specific I am with the date, the more likely I am to have a collision (e.g. if two posts are published on the same day). I could have easily added &lt;code class=&quot;highlighter-rouge&quot;&gt;hour-minute-second&lt;/code&gt; to the date to be more specific, and that probably would work fine, but I didn’t want the date so blatantly displayed within the URL. After all, these are &lt;em&gt;permalinks&lt;/em&gt; and I strive to may my content evergreen. I don’t want users thinking my content is outdated because the URL contains an old date.&lt;/p&gt;

&lt;p&gt;It seems like Medium is using an ID in base64 notation, so I decided to encode the date using MD5 and then truncate the hash to a limited number of characters. To achieve this, I wrote a small Jekyll extension called &lt;a href=&quot;https://github.com/joeyhoer/jekyll-hashpermalink&quot;&gt;jekyll-hashpermalink&lt;/a&gt; which adds a &lt;code class=&quot;highlighter-rouge&quot;&gt;:static_id&lt;/code&gt; variable that could be used within the Jekyll permalink.&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-yml&quot; data-lang=&quot;yml&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;permalink&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;/:categories/:title-:static_id'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Having added this new variable, I was able to append a static ID to my URLs, but this still did not solve the problem. If I changed the post title, the old URL would become invalid. I want a solution where the title can change &lt;em&gt;without&lt;/em&gt; invalidating the URL. Effectively, I need the title to be ignored, such that only the &lt;code class=&quot;highlighter-rouge&quot;&gt;static ID&lt;/code&gt; is used to serve content.&lt;/p&gt;

&lt;p&gt;To achieve this next step, I had to change the way Jekyll creates the file structure when creating posts during compilation by overwriting the &lt;code class=&quot;highlighter-rouge&quot;&gt;Jekyll::Document destination&lt;/code&gt; method. Instead of using the full &lt;code class=&quot;highlighter-rouge&quot;&gt;url&lt;/code&gt;, which is Jekyll’s default destination, I replace this with just the &lt;code class=&quot;highlighter-rouge&quot;&gt;static_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once the files were writing to the correct location, they could be accessed using the &lt;code class=&quot;highlighter-rouge&quot;&gt;static_id&lt;/code&gt; as the URL, but they could no longer be accessed with the full URL (including their title). To resolve this problem, I had to configure my server to serve content based on the static ID suffix rather than the full URL.&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_URI} -([^-]*)$
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; ^ %1/index.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Caution:&lt;/strong&gt; It may be pertinent to ignore specific subdirectories (e.g. the &lt;code class=&quot;highlighter-rouge&quot;&gt;assets&lt;/code&gt; directory), or be more specific with the patten match to prevent unintended URLs from being rewritten.&lt;/p&gt;

&lt;p&gt;Finally, I could successfully access all of my content using the static ID, and I could change the title at will without needing to manually define a 301 redirect after each change.&lt;/p&gt;

&lt;h2 id=&quot;duplicate-content&quot;&gt;Duplicate Content&lt;/h2&gt;

&lt;p&gt;To prevent duplicate content issues, I ensured that a canonical link was present on the page that points to the URL with the latest title.&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-liquid&quot; data-lang=&quot;liquid&quot;&gt;&lt;code&gt;&amp;lt;link rel=&quot;canonical&quot; href=&quot;&lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'index.html'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;site.baseurl&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;prepend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;site.url&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;always-accurate&quot;&gt;Always Accurate&lt;/h2&gt;

&lt;p&gt;I also added a tiny bit of JavaScript to rewrite the URL in browser, using &lt;code class=&quot;highlighter-rouge&quot;&gt;history.replaceState&lt;/code&gt;, so that the URL the visitor sees will always match the current correct canonical URL, regardless of the URL they used to access the page.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-js&quot; data-lang=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Shareable URLs&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Automatically replace URL with canonical URL&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Strip query parameters from the URL&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;canonical_pathname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;document&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;querySelector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'link[rel=canonical]'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;/^.*&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\/\/[^\/]&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt;+/&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replaceState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({},&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;canonical_pathname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;||&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pathname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)})(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Unfolding the Fold</title>
   <link href="https://joeyhoer.com/unfolding-the-fold-bead1166"/>
   <updated>2019-02-11T14:01:22-05:00</updated>
   <id>https://joeyhoer.com/unfolding-the-fold</id>
   <content type="html">&lt;p&gt;When discussing web design, I often hear clients refer to “the fold.” Generally there is some important element that they would like displayed prominently on a page, and they will request that the element is placed “above the fold.” As a frontend developer experienced in responsive web design, knowing that viewpoint sizes vary greatly, this concept of &lt;em&gt;the fold&lt;/em&gt; is difficult to grasp. When we say “the fold”, what exactly are we referring to? How can we respectfully share our expertise with clients to arrive at a common understanding of how &lt;em&gt;the fold&lt;/em&gt; applies to modern web design?&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;what-is-the-fold&quot;&gt;What is “the fold”?&lt;/h2&gt;

&lt;p&gt;The term “above the fold” originated in newspaper print design and refers to the content that is visible on the front page of the newspaper when it is folded. Usually this section of the page includes an intriguing headline accompanied by a large image. The goal of this content is to entice readers to pick up the paper and open it.&lt;/p&gt;

&lt;p&gt;On the web, “the fold” similarly refers to the content visible on the page without scrolling. People who believe in “the digital fold” will say that the intention is the same—placing compelling content “above the fold” encourages visitors to read on and become more engaged with the website.&lt;/p&gt;

&lt;h1 id=&quot;the-bottom-line&quot;&gt;The Bottom Line&lt;/h1&gt;

&lt;p&gt;Content &lt;em&gt;should&lt;/em&gt; be prioritized such that “above the fold” content demonstrates immediate value to the visitor, reaffirming their decision to visit the page.&lt;/p&gt;

&lt;p&gt;That is not to say that all content, even the most important content must be placed above the fold. Simply that the first content which is displayed to the user should give them confidence that the page they are visiting contains relevant information to the content which they are seeking.&lt;/p&gt;

&lt;h2 id=&quot;the-digital-fold&quot;&gt;The Digital Fold&lt;/h2&gt;

&lt;p&gt;The main problem with the digital fold is that it is inconsistent. When a newspaper designer creates a layout for the front page, they know exactly how that content will appear to the reader—the medium is fixed. The printed broadsheet will always be 749mm by 597mm, and those physical dimensions will never change. This assumption cannot be made on the web. On the web, &lt;a href=&quot;https://iamthefold.com&quot;&gt;the fold&lt;/a&gt; will be very different on a hand-held device in landscape orientation when compared to an ultrawide desktop monitor in portrait orientation. A browser window may cover the entire screen, or just a portion of the screen. Simply put, the digital fold is unpredictable. With all of these various viewports (browser window sizes) being used to display the same content, it is impractical to believe that content which appears “above the fold” can be controlled.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; Modern browsers support viewport units which can allow designs to be created relative to the “above the fold” section. However, the usability of such a design may be questionable when presented within different contexts (viewports).&lt;/p&gt;

&lt;p&gt;It’s easy to say “&lt;a href=&quot;https://unbounce.com/mobile-optimization/there-is-no-fold/&quot;&gt;there is no fold&lt;/a&gt;.” Afterall, &lt;a href=&quot;http://thereisnofold.tumblr.com&quot;&gt;there is no fold&lt;/a&gt;. However, true as that may be, stating that fact does nothing to address very real concerns that the first content a user sees on a page is a determining factor in their assessment of the page’s &lt;em&gt;credibility&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It is important to understand that &lt;a href=&quot;https://www.hugeinc.com/articles/everybody-scrolls&quot;&gt;everybody scrolls&lt;/a&gt;, as scrolling is a natural affordance of a webpage. However, the longer it takes a user to locate relevant content on the page, the more likely they are to abandon the page. For this reason, when conversations around “above the fold” content arise, it is important to steer the conversation towards a content planning and prioritization exercise rather than towards a design discussion, as the act of prioritizing content such that it is likely to quickly answer a visitor’s question will produce better results than attempting to perfect above the fold content/design across every viewport size.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The Effective Use of Story Points</title>
   <link href="https://joeyhoer.com/effective-use-of-story-points-5ab4a0dc"/>
   <updated>2018-12-27T20:55:07-05:00</updated>
   <id>https://joeyhoer.com/effective-use-of-story-points</id>
   <content type="html">&lt;p&gt;When managing a project of known scope, understanding the time required to complete the project (or a piece of the project) with available resources is critical to the ability to plan effectively and appropriately set expectations with stakeholders.&lt;/p&gt;

&lt;p&gt;Naturally, workers of differing skill levels will complete a given task at different rates—i.e. a highly skilled worker will work more quickly than a low skilled worker. This fundamental truth of labor can present a significant challenge when estimating timelines in man-hours, as estimates produced by one worker may not hold true for another.&lt;/p&gt;

&lt;p&gt;Story points may be used as an alternative to man-hours when planning projects, but before we start using story points, we must understand what story points are and why they are useful.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h1 id=&quot;what-is-a-story-point&quot;&gt;What is a story point?&lt;/h1&gt;

&lt;p&gt;At the most basic level, a story point is a unit of measurement. Story points measure &lt;em&gt;the effort&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/em&gt; required to complete a piece of work.&lt;/p&gt;

&lt;p&gt;More accurately story points are an &lt;em&gt;estimate&lt;/em&gt; of effort, as story points consider the fact that attempting to predict the future has inherent uncertainties. Story points do not attempt to be precise at a micro level (i.e. tasks), but allow more accurate predictions to be made at a macro level (i.e. projects).&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; The term “estimate” is used in this article to refer to level-of-effort estimates used in project planning, these types of estimates are not intended to be used directly in a sales context and do not estimate monetary cost.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; For the remainder of this article I will use “points” and “story points” interchangeably.&lt;/p&gt;

&lt;h2 id=&quot;what-are-story-points-used-for&quot;&gt;What are story points used for?&lt;/h2&gt;

&lt;p&gt;Story points are typically used in Agile project management to make predictions about the amount of time required to complete a project with known resources (i.e. a given set of workers of known skill levels).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Story points are about time. There, I’ve said it, and can’t be more clear than that. … The primary reason for estimating product backlog items is so that predictions can be made about how much functionality can be delivered by what date. If we want to estimate what can be delivered by when, we’re talking about time.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://www.mountaingoatsoftware.com/blog/story-points-are-still-about-effort&quot;&gt;Story Points Are Still About Effort&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;making-timeline-predictions-with-story-points&quot;&gt;Making timeline predictions with story points&lt;/h2&gt;

&lt;p&gt;To predict the remaining time required to complete a project using story points we must have two things:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;We must know how much work must be done (i.e. the project estimate)&lt;/li&gt;
  &lt;li&gt;We must understand the team’s &lt;em&gt;velocity&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;“Velocity” is the number of points a team has historically completed over a given amount of time (commonly a one-week or two-week “sprint”).&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; I like to refer to the number of points an individual has completed over a given amount of time as that individual’s &lt;em&gt;“throughput”&lt;/em&gt;. Though “individual velocity” may be a more correct term within Agile, I’ll use “throughput” in this article to differentiate between team velocity and individual velocity.&lt;/p&gt;

&lt;p&gt;In the simplest terms, we can predict duration by dividing the total number of points by the team’s historical velocity.&lt;/p&gt;

&lt;math xmlns=&quot;&amp;mathml;&quot;&gt;
  &lt;mrow&gt;
    &lt;mi&gt;estimated duration&lt;/mi&gt;
    &lt;mo&gt;=&lt;/mo&gt;
    &lt;mfrac&gt;
      &lt;mi&gt;total points&lt;/mi&gt;
      &lt;mi&gt;velocity&lt;/mi&gt;
    &lt;/mfrac&gt;
  &lt;/mrow&gt;
&lt;/math&gt;

&lt;p&gt;For example, if there are 100 story points remaining within a project, and our team can complete 20 points per week, we can expect that the project will require 5 weeks to complete.&lt;/p&gt;

&lt;math xmlns=&quot;&amp;mathml;&quot;&gt;
  &lt;mrow&gt;
    &lt;mfrac&gt;
      &lt;mi&gt;100 points&lt;/mi&gt;
      &lt;mi&gt;20 points/week&lt;/mi&gt;
    &lt;/mfrac&gt;
    &lt;mo&gt;=&lt;/mo&gt;
    &lt;mi&gt;5 weeks&lt;/mi&gt;
  &lt;/mrow&gt;
&lt;/math&gt;

&lt;h1 id=&quot;story-points-vs-man-hours&quot;&gt;Story Points vs. Man-Hours&lt;/h1&gt;

&lt;p&gt;At this point, you might be wondering how using story points in this way is different from using man-hours—after all, if we have a project estimated at 100 hours, and our team can devote 20 hours per week to a project, then we can also predict the project will require 5 weeks to complete.&lt;/p&gt;

&lt;p&gt;The crux of the issue is that, as we know, workers of differing skill levels will complete a given task at different rates. Estimating in man-hours tightly couples the skill set of an individual worker to the estimate. For this reason, hours-based estimates must be re-estimated when team assignments change (e.g. when team members are added or removed during the project), or when challenges emerge that impede the team’s performance.&lt;/p&gt;

&lt;p&gt;Story points decouple estimates from individual workers, providing an abstraction to managers which offers more information about remaining effort. This allows managers to make more informed decisions when allocating resources. Using this method, level-of-effort estimates need not change when the team changes; all that must be done to recalculate the predicted timeline is to update the team’s velocity.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Let’s consider an example: Parker, an expert programmer, and Pat, a new employee, are both tasked individually with configuring a web server. The following table shows the actual hours each employee spent on the task.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Task&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Parker’s Hours&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Pat’s Hours&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Configure a web server&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;4 hours&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;8 hours&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Morgan, their manager, is asked by a new client, “How long will it take to configure a new web server?” … To answer this question accurately, Morgan must understand who is assigned to the task. This underscores the fact that duration is always a function of the resources assigned to a task. If resources change, when using hours-based estimates, the timeline required for completion must also be re-estimated.&lt;/p&gt;

&lt;p&gt;Taking this example further—the client makes a request to build a new microsite. We want Parker and Pat to work on the project together, and we understand that this task is roughly 10 times the effort of configuring a web server. How quickly can we deliver this project? … Stumped? This too illustrates the difficulty of using man-hours for estimating completion.&lt;/p&gt;

&lt;p&gt;Let’s try using story points to answer this question instead. First, we must have an agreed upon baseline to calibrate our story points. We will use the task of configuring a web server as the baseline, and assign 4 points to this task.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Task&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Story Points&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Configure a web server&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;4 points&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Build a microsite&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;40 points&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; The number of points assigned to the baseline task is not important, so long as we can measure other tasks accurately in relation to that task.&lt;/p&gt;

&lt;p&gt;Remembering that duration is always a function of the resources assigned to the task, we must also determine how many story points both Parker and Pat are able to complete within a given time frame (throughput). Based on their previous work configuring the server, we can infer the following:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt;Team Member&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Throughput&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Parker&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1 point/hour&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Pat&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;0.5 point/hour&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; The conversion rate between points and hours again is insignificant, so long as it is consistent across the team.&lt;/p&gt;

&lt;p&gt;We can use this information to determine the team’s velocity by combining  Parker and Pat’s throughput.&lt;/p&gt;

&lt;math xmlns=&quot;&amp;mathml;&quot;&gt;
  &lt;mrow&gt;
    &lt;mi&gt;1 point/hour&lt;/mi&gt;
    &lt;mo&gt;+&lt;/mo&gt;
    &lt;mi&gt;0.5 points/hour&lt;/mi&gt;
    &lt;mo&gt;=&lt;/mo&gt;
    &lt;mi&gt;1.5 points/hour&lt;/mi&gt;
  &lt;/mrow&gt;
&lt;/math&gt;

&lt;p&gt;We now have all the information we need to answer the question “how long will it take Ted and Pat to build a microsite?”&lt;/p&gt;

&lt;math xmlns=&quot;&amp;mathml;&quot;&gt;
  &lt;mrow&gt;
    &lt;mfrac&gt;
      &lt;mi&gt;40 points&lt;/mi&gt;
      &lt;mi&gt;1.5 points/hour&lt;/mi&gt;
    &lt;/mfrac&gt;
    &lt;mo&gt;=&lt;/mo&gt;
    &lt;mi&gt;26.67 hours&lt;/mi&gt;
  &lt;/mrow&gt;
&lt;/math&gt;

&lt;p&gt;In this way, managers may more easily and accurately predict timelines. Additionally, managers may reassign resources without having to reestimate individual tasks.&lt;/p&gt;

&lt;p&gt;For instance, suppose Morgan needs to reassign Parker at the last minute, and replaces Parker with Jamie, an experienced programmer. Jamie can configure a server in 6 hours, and therefor has a throughput of 0.75 points/hour. Let’s see how this impacts the timeline:&lt;/p&gt;

&lt;math xmlns=&quot;&amp;mathml;&quot;&gt;
  &lt;mrow&gt;
    &lt;mfrac&gt;
      &lt;mi&gt;40 points&lt;/mi&gt;
      &lt;mi&gt;1.25 points/hour&lt;/mi&gt;
    &lt;/mfrac&gt;
    &lt;mo&gt;=&lt;/mo&gt;
    &lt;mi&gt;32 hours&lt;/mi&gt;
  &lt;/mrow&gt;
&lt;/math&gt;

&lt;p&gt;Morgan can now appropriately set stakeholders’ expectations or assign additional resources to the project.&lt;/p&gt;

&lt;h1 id=&quot;points-to-actual-hours-conversion&quot;&gt;Points to Actual Hours Conversion&lt;/h1&gt;

&lt;p&gt;Recall that story points are a measure of effort. Story points make the assumption that, over time, the average velocity of the team—the amount of effort they’re able to invest—will be reasonably constant. Assuming point value estimates are consistent across the team, estimates based on points and historical velocity should be fairly accurate and should not fluctuate wildly. Additionally, the longer this process is used, the more accurate it becomes.&lt;/p&gt;

&lt;p&gt;Unfortunately, time is not a precise measurement of effort—as we’ve seen, one “one point” task might require 30 minutes to complete while another “one point” task might require two hours to complete, depending upon a number of factors. More accurately, one point equals a range of possible values representing a normal distribution centralized on a mean with some standard deviation. For this reason, &lt;a href=&quot;https://www.mountaingoatsoftware.com/blog/how-do-story-points-relate-to-hours&quot;&gt;story points cannot be directly translated into actual hours.&lt;/a&gt; It is possible to determine the mean, though it may not always be accurate.&lt;/p&gt;

&lt;div class=&quot;placeholder &quot; style=&quot;--aspect-ratio:calc(100%/(756/373))&quot;&gt;
  &lt;div style=&quot;max-width:756px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Normal distribution of one and two point tasks&quot; width=&quot;756&quot; height=&quot;373&quot; integrity=&quot;sha256-TIcuDASiX3jinT2uBKGODPjN2a+RXEPXcHRl+pWTtrc=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/story-points-normal-distribution-4c872e0c04a25f78e29d3dae04a18e0cf8cdd9af915c43d7707465fa9593b6b7.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;The most important attribute of a story point is that it is a common unit of measurement across the entire team. A unit of measurement must remain constant to be an effective tool. If point estimates vary between team members then velocity cannot be tracked accurately, nor can velocity be used to make future predictions about expected timeline. Story points are meaningless if the team does not have general consensus on the value of a story point.&lt;/p&gt;

&lt;h2 id=&quot;calibrating-the-team&quot;&gt;Calibrating the Team&lt;/h2&gt;

&lt;p&gt;Estimating in story points requires commitment from the entire team. Everyone must agree on what “1 story point” represents in terms of effort. Before a team can effectively use story points, the team must be “calibrated” so each member shares a common understanding of the value of a point. Selecting a baseline that is commonly understood will make calibrating the team easier.&lt;/p&gt;

&lt;p&gt;When a team is properly calibrated, a points estimate should be able to be validated with any team member, and that team member should arrive at (roughly) the same point value assigned. &lt;a href=&quot;https://en.wikipedia.org/wiki/Planning_poker&quot;&gt;Planning poker&lt;/a&gt;, a variation of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Wideband_delphi&quot;&gt;Wideband Delphi method&lt;/a&gt;, is one effective method for calibrating a team.&lt;/p&gt;

&lt;p&gt;Another approach to calibrate the team is to use the highest or lowest performer to set the baseline. It may be easier for the team to agree “it should only take a high performer &lt;em&gt;X&lt;/em&gt; hours to complete this task.”  This was demonstrated in the previous example where Parker had a 1-to-1 points-to-hours ratio.&lt;/p&gt;

&lt;h1 id=&quot;communicating-with-stakeholders&quot;&gt;Communicating with Stakeholders&lt;/h1&gt;

&lt;p&gt;Presenting clients estimates in hours can establish expectations which may be difficult to revise. Because story points abstract hours estimates, clients cannot draw conclusions between story points and hours. However story points do offer the client the ability to compare the relative size of one task with another.&lt;/p&gt;

&lt;p&gt;While this approach may effectively move focus away from hours estimates, it does not address the client’s primary concerns—namely, the price of a project, and the delivery date. If work is priced commensurate to the value the customer receives, if timeline is adequately communicated, and if there is trust established based on a history of meeting deadlines, then inputs (man-hours) will be irrelevant to the client.&lt;/p&gt;

&lt;p&gt;The opacity of story points makes them an ineffective tool for managing client expectations. Timelines should always be delivered to the client as delivery dates rather than as a number of hours or number of points.&lt;/p&gt;

&lt;hr /&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;&lt;a href=&quot;https://www.mountaingoatsoftware.com/blog/its-effort-not-complexity&quot;&gt;Story points &lt;em&gt;do not&lt;/em&gt; measure complexity.&lt;/a&gt; Complexity is merely one factor which is considered when determining the level of effort—in addition to risk and uncertainty. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Thoughts on AMP</title>
   <link href="https://joeyhoer.com/thoughts-on-amp-06785350"/>
   <updated>2018-09-26T14:06:18-04:00</updated>
   <id>https://joeyhoer.com/thoughts-on-amp</id>
   <content type="html">&lt;p&gt;Google has &lt;a href=&quot;https://joeyhoer.com/good-ux-is-good-seo&quot;&gt;a long standing tradition&lt;/a&gt; of introducing changes to it’s search engine designed to enhance user experience, and Google’s dominance as a search provider essentially mandates the adoption of Google’s prescribed best practices. AMP promises to make the web faster, but embracing AMP might be more complicated than you would expect.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h1 id=&quot;what-is-amp&quot;&gt;What is AMP?&lt;/h1&gt;

&lt;p&gt;Accelerated Mobile Pages (AMP) is a web content publishing format, developed by Google, which aims to improve user experience on mobile devices by optimizing webpages for performance. This is accomplished using a non-standard subset of HTML which provides only limited access to JavaScript and other native web technologies. The most common implementation of AMP is to use separate page templates for AMP, though this is not a requirement of AMP.&lt;/p&gt;

&lt;div class=&quot;placeholder &quot; style=&quot;--aspect-ratio:calc(100%/(800/529))&quot;&gt;
  &lt;div style=&quot;max-width:800px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Screenshots of different versions of an AMP-enabled page: original page, AMP page, and AMP cache page&quot; width=&quot;800&quot; height=&quot;529&quot; integrity=&quot;sha256-2GOWCBenk3TMa9BHVX6rsqe+uEXXhMvLZE9fx5iAItw=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/amp-pages-d863960817a79374cc6bd047557eabb2a7beb845d784cbcb644f5fc7988022dc.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;Here is a typical AMP setup:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;Original/Canonical URL:&lt;/strong&gt; &lt;a href=&quot;https://www.wired.com/story/google-artificial-intelligence-monopoly/&quot;&gt;https://www.wired.com/story/google-artificial-intelligence-monopoly/&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;AMP URL:&lt;/strong&gt; &lt;a href=&quot;https://www.wired.com/story/google-artificial-intelligence-monopoly/amp&quot;&gt;https://www.wired.com/story/google-artificial-intelligence-monopoly/amp&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;AMP Cache (Google)&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/strong&gt; &lt;a href=&quot;https://www.google.com/amp/s/www.wired.com/story/google-artificial-intelligence-monopoly/amp&quot;&gt;https://www.google.com/amp/s/www.wired.com/story/google-artificial-intelligence-monopoly/amp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; In May 2018 &lt;a href=&quot;https://www.ampproject.org/latest/blog/a-first-look-at-using-web-packaging-to-improve-amp-urls/&quot;&gt;Google announced plans&lt;/a&gt; to incorporate new technology into AMP that would allow content creators to use their own URLs within Google’s cache.&lt;/p&gt;

&lt;p&gt;Despite the fact that Google is primarily responsible for both &lt;a href=&quot;https://www.ampproject.org&quot;&gt;the AMP format&lt;/a&gt; and &lt;a href=&quot;https://developers.google.com/amp/cache/&quot;&gt;an AMP cache implementation within Google search results&lt;/a&gt;, it is important to understand that the format and cache implementation are technically independent elements. Other platforms like &lt;a href=&quot;https://developer.twitter.com/en/docs/publisher-tools/amp/overview.html&quot;&gt;Twitter&lt;/a&gt; and &lt;a href=&quot;https://amp.cloudflare.com&quot;&gt;Cloudflare&lt;/a&gt; offer their own AMP cache implementations, but for the purposes of this article, “AMP” refers to Google’s AMP cache implementation.&lt;/p&gt;

&lt;p&gt;AMP is a polarizing topic which has received &lt;a href=&quot;http://ampletter.org/&quot;&gt;widespread criticism&lt;/a&gt; from the web development community. Some of the criticism stems from the fact that Google has created a “closed” format which is not governed by an open standards organization; this allows the AMP team to move quickly, but does not create an inviting atmosphere for community involvement. Additional controversy is precipitated by Google’s use of their dominant market position to drive the adoption of the format by giving AMP pages preferential treatment within mobile search results. Furthermore, Google’s management of content served from the AMP cache, which retains users within Google’s own domain, has also fueled frustrations.&lt;/p&gt;

&lt;h2 id=&quot;why-does-amp-exist&quot;&gt;Why does AMP exist?&lt;/h2&gt;

&lt;p&gt;Colin Chapman, the founder of Lotus Cars had a famous philosophy: “simplify, then add lightness.“ He contended that while ”adding power makes you faster on the straights, subtracting weight makes you faster everywhere.” This is the lens through which we examine website performance—less is more.&lt;/p&gt;

&lt;p&gt;Unfortunately, with ever-increasing pressure to drive results, website managers are driven to seek out any opportunity that claims to show a positive return on investment. With a multitude of providers each claiming that adding their service will improve conversion and provide more powerful user insights, content creators are easily convinced that adding &lt;em&gt;more&lt;/em&gt; to their websites is the answer to their problems. In this way too, website managers can easily demonstrate progress to their supervisors. As these additions accumulate, site performance decreases, leading to an unpleasant user experience. By this point, the damage has been done—it’s difficult to justify getting rid of anything that’s claiming to produce a positive return on investment, even when those claims are difficult or impossible to substantiate. As it turns out, convincing content creators to add functionality to their website is easy, convincing them to remove functionality—which is generally a requirement of improving performance—is hard.&lt;/p&gt;

&lt;p&gt;So here we are. While web practitioners have long been aware of the importance of site speed, incentives and penalties like &lt;a href=&quot;https://webmasters.googleblog.com/2010/04/using-site-speed-in-web-search-ranking.html&quot;&gt;Google’s Site Speed algorithm update (2010)&lt;/a&gt; did little to curb the ever-increasing size of webpages. Since that update, &lt;a href=&quot;https://httparchive.org/reports/page-weight?start=2011_06_01&amp;amp;end=latest&amp;amp;view=list#reqTotal&quot;&gt;the average number of assets requested on a mobile webpage increased by over 140%&lt;/a&gt; (from 29 to 70). Over the same period, &lt;a href=&quot;https://httparchive.org/reports/page-weight?start=2011_06_01&amp;amp;end=latest&amp;amp;view=list#bytesTotal&quot;&gt;the size of the average mobile webpage increased over 725%&lt;/a&gt; (from 200KB to nearly 1700KB). Despite the fact that there is plenty of data which supports the idea that better page performance equates to more time onsite and increased conversion rates, most organizations have not defined a performance budget to educate decisions and protect site speed and user experience, so webpages grow larger while the user experience becomes slower.&lt;/p&gt;

&lt;p&gt;By failing to improve performance, content creators are missing out on opportunities. Instead, tech giants introduced their own solutions which redistribute content with improved performance—Facebook (via &lt;a href=&quot;https://developers.facebook.com/docs/instant-articles&quot;&gt;Instant Articles&lt;/a&gt;), Google and &lt;a href=&quot;https://blog.twitter.com/2015/introducing-accelerated-mobile-pages-0&quot;&gt;Twitter&lt;/a&gt; (via &lt;a href=&quot;https://www.ampproject.org/&quot;&gt;AMP&lt;/a&gt;), and Apple (via &lt;a href=&quot;http://www.apple.com/news/&quot;&gt;the News app&lt;/a&gt;)—which allow them to keep users on their platforms longer, and make more money through their own advertising.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[T]he emphasis on speed… with things like Instant Articles, native is making the browser-based web look like a relic even just for publishing articles… [which] might pose a problem even for my overwhelmingly-text work. [My website’s] pages load fast, but the pages I link to often don’t. I worry that the inherent slowness of the web and ill-considered trend toward over-produced web design is going to start hurting traffic to [my site].&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://daringfireball.net/2015/05/facebook_instant_articles&quot;&gt;Facebook Introduces Instant Articles&lt;/a&gt; by John Gruber&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The depressing truth of why AMP exists is that Google gave the community a chance to make the web faster—we didn’t. Instead we broke &lt;a href=&quot;https://joeyhoer.com/the-appropriate-use-of-modals&quot;&gt;another&lt;/a&gt; piece of the web. In it’s brilliance, &lt;em&gt;AMP is a simplification masquerading as an enhancement.&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;benefits-of-amp&quot;&gt;Benefits of AMP&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;AMP’s biggest advantage is the restrictions it draws on how much &lt;em&gt;stuff&lt;/em&gt; you can cram into a single page.&lt;/p&gt;

  &lt;p&gt;— &lt;a href=&quot;https://timkadlec.com/remembers/2018-03-19-how-fast-is-amp-really/&quot;&gt;How Fast Is AMP Really?&lt;/a&gt; by Tim Kadlec&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Google has encouraged adoption of the AMP format by offering content creators increased readership through heightened visibility within search results. This occurs in a number of ways:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;A lightning bolt icon is displayed alongside AMP content within mobile search results.&lt;/li&gt;
  &lt;li&gt;AMP content is eligible to be promoted into AMP carousels within mobile search results (including the “Top Stories” carousel which is prominently displayed).&lt;/li&gt;
  &lt;li&gt;AMP content is cached by Google, served through it’s CDN, and preloaded within search results, allowing content to load “instantly”.&lt;/li&gt;
&lt;/ol&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Speculation:&lt;/strong&gt; Presumably the preferred placement in mobile search also impacts ranking in non-mobile search results, as &lt;a href=&quot;https://webmasters.googleblog.com/2016/11/mobile-first-indexing.html&quot;&gt;Google announced&lt;/a&gt; that the “mobile index” would become the primary source of site rank in November 2016.&lt;/p&gt;

&lt;h2 id=&quot;criticisms-of-amp&quot;&gt;Criticisms of AMP&lt;/h2&gt;

&lt;p&gt;Critics of AMP believe that these benefits are an unfair &lt;a href=&quot;https://ferdychristant.com/amp-the-missing-controversy-3b424031047&quot;&gt;anti competitive behavior&lt;/a&gt; due to the fact that non-AMP pages, which can be made to be faster than their AMP counterparts, do not get access to the same benefits as AMP pages. The result of preloading AMP content within search results is what allows AMP pages to load instantly; publishers can’t compete with that.&lt;/p&gt;

&lt;p&gt;There is an axiom: “There is no right way to do the wrong thing.” I wonder now if the reverse is also true, or if Google has managed to do the right thing the wrong way. Google was able to leverage it’s search dominance to address performance in a way that no one else could, by offering business owners valuable incentives. However, because only AMP pages receive preferential treatment, only AMP pages are “instant”. Until business owners are convinced that overall performance is important, the web at large will continue to grow larger and load slower, which is the real problem that needs to be addressed.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;AMP hasn’t solved the core problem; it has merely hidden it a little bit. … the incentives being placed on AMP content seem to be accomplishing exactly what you would think: they’re incentivizing AMP, not performance.&lt;/p&gt;

  &lt;p&gt;— &lt;a href=&quot;https://timkadlec.com/remembers/2018-03-19-how-fast-is-amp-really/&quot;&gt;How Fast Is AMP Really?&lt;/a&gt; by Tim Kadlec&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In fact, even Malte Ubl, creator and tech lead of the AMP Project, has said “&lt;a href=&quot;https://twitter.com/cramforce/status/925364477071040512&quot;&gt;make AMP your own site.&lt;/a&gt;” The solution to addressing the problem of performance is to make your site faster, not to focus on AMP.&lt;/p&gt;

&lt;p&gt;Content creators should also understand the trade-offs they are making when choosing to implement AMP. Unless content creators are willing to rebuild their existing websites, eliminating any functionality not supported by AMP, then publishers will need to assume the costs of creating and maintaining a second AMP version of each of their webpages. &lt;em&gt;(Does this remind anyone else of m-dot mobile sites?)&lt;/em&gt; By using a closed format, and allowing Google to host their content, publishers are ceding control of their content to Google. Due to the nature of Google’s current AMP cache implementation, whereby AMP content is served from the &lt;code class=&quot;highlighter-rouge&quot;&gt;google.com&lt;/code&gt; domain, publishers are accepting that their website (the content origin) may receive less traffic.&lt;/p&gt;

&lt;h2 id=&quot;how-does-google-benefit-from-amp&quot;&gt;How does Google benefit from AMP?&lt;/h2&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Notice:&lt;/strong&gt; This section contains speculation.&lt;/p&gt;

&lt;p&gt;I don’t find it too hard to believe that Google can or will use AMP formatted content to fuel it’s AI initiatives. Scraping the web is a complicated business—enforcing a limited standard would allow Google to crawl and scrape content more easily, and use this data to power &lt;a href=&quot;https://support.google.com/webmasters/answer/6229325?hl=en&quot;&gt;Google’s “Featured Snippets,”&lt;/a&gt; which are used to power the &lt;a href=&quot;https://joeyhoer.com/the-url-is-dead-long-live-the-url#conversational-ui&quot;&gt;conversational UI&lt;/a&gt; of Google Assistant. &lt;a href=&quot;http://searchengineland.com/google-officially-adds-amp-based-featured-snippets-mobile-search-results-282005&quot;&gt;Google confirmed that AMP links may be displayed in featured snippets of mobile search results&lt;/a&gt; in September 2017.&lt;/p&gt;

&lt;p&gt;We also know that slow sites lead to fewer page views which lead to fewer ad impressions which results in fewer dollars in the pockets of ad platforms like Google. Google has a vested interest in maintaining a dominant position in digital advertising. In fact, the AMP format with all of it’s limitations on JavaScript and functionality, &lt;a href=&quot;https://www.ampproject.org/latest/blog/growing-the-amp-ads-initiative/&quot;&gt;does support a variety of ads&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Of course, by writing the code for these new ad formats, [Google is] also putting itself in the middle of how those ads will be implemented, giving Google an ongoing place at the table for how the next generation of the mobile web will monetize.&lt;/p&gt;

  &lt;p&gt;— &lt;a href=&quot;https://techcrunch.com/2017/05/18/googles-amp-now-powers-2b-mobile-pages-and-900k-domains-loads-2x-faster/&quot;&gt;Ingrid Lunden&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;speed-without-amp&quot;&gt;Speed without AMP&lt;/h2&gt;

&lt;p&gt;As a parting thought for any reader who is considering implementing AMP, I’d like to share some wisdom from a very entertaining talk by Maciej Cegłowski called &lt;a href=&quot;https://idlewords.com/talks/website_obesity.htm&quot;&gt;The Website Obesity Crisis&lt;/a&gt; which succinctly describes how to build a fast website:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Nutritionists used to be big on this concept of a food pyramid. I think we need one for the web, to remind ourselves of what a healthy site should look like. … Here is what I recommend for a balanced website …:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;A solid base of text worth reading, formatted with a healthy dose of markup.&lt;/li&gt;
    &lt;li&gt;Some images, in moderation, to illustrate and punch up the visual design.&lt;/li&gt;
    &lt;li&gt;A dollop of CSS.&lt;/li&gt;
    &lt;li&gt;And then, very sparingly and only if you need it, JavaScript.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;… my simple two-step secret to improving the performance of any website.&lt;/p&gt;

  &lt;ol&gt;
    &lt;li&gt;Make sure that the most important elements of the page download and render first.&lt;/li&gt;
    &lt;li&gt;Stop there.&lt;/li&gt;
  &lt;/ol&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;
      &lt;p&gt;To visit Google’s cached AMP page, you must be on a mobile device. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Static Site URL Shortener</title>
   <link href="https://joeyhoer.com/static-site-url-shortener-3c0d454e"/>
   <updated>2017-10-30T10:27:17-04:00</updated>
   <id>https://joeyhoer.com/static-site-url-shortener</id>
   <content type="html">&lt;p&gt;On Apache servers, a &lt;code class=&quot;highlighter-rouge&quot;&gt;RedirectMap&lt;/code&gt; may be used to import bulk redirects. This provides an ideal solution for creating a self-hosted short URL service for any website, particularly static sites generated with Jekyll.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;To implement a URL shortener using Jekyll and Apache, begin by simply adding a new key (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;shorturl&lt;/code&gt;) to the front matter of any relevant posts. For example:&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;shorturl&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;example&lt;/span&gt;
&lt;span class=&quot;nn&quot;&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, create a new file (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;shorturls.txt&lt;/code&gt;) which contains the following &lt;code class=&quot;highlighter-rouge&quot;&gt;liquid&lt;/code&gt; code:&lt;/p&gt;

&lt;div class=&quot;language-liquid highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-liquid&quot; data-lang=&quot;liquid&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;post&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;in&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;site.posts&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shorturl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shorturl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;endif&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;endfor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;page&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;in&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;site.pages&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shorturl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;shorturl&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;site&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}{{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;remove&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;index.html&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;endif&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}{%&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;endfor&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Build the site. Once the site is built, a new text file should exist resembling the following:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;example&lt;/span&gt; https://www.example.com/this-is-an-example
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Once the redirect map file is created, instruct Apache to use the file by adding the &lt;code class=&quot;highlighter-rouge&quot;&gt;RedirectMap&lt;/code&gt; to the Apache configuration (typically in &lt;code class=&quot;highlighter-rouge&quot;&gt;httpd.conf&lt;/code&gt;, or an included &lt;code class=&quot;highlighter-rouge&quot;&gt;vhost&lt;/code&gt;) by using the following:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;IfModule&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt; mod_rewrite.c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;  &lt;span class=&quot;nc&quot;&gt;RewriteEngine&lt;/span&gt; On
  &lt;span class=&quot;nc&quot;&gt;RewriteBase&lt;/span&gt; /
  
  &lt;span class=&quot;c&quot;&gt;# IMPORTANT: Make sure this file exists&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;# RewriteMap shorturl txt:/path/to/shorturls.txt&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;RewriteMap&lt;/span&gt; shorturl dbm:/path/to/shorturls.map

  &lt;span class=&quot;c&quot;&gt;# URL shortener with fallback&lt;/span&gt;
  &lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; ^(.*)$ ${shorturl:$1|https://example.com/$1} [R=302,L]
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;IfModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code class=&quot;highlighter-rouge&quot;&gt;RewriteRule&lt;/code&gt; above uses &lt;code class=&quot;highlighter-rouge&quot;&gt;302&lt;/code&gt; redirects when redirecting, and allows non-existent paths to fall through/fallback to the unshortened URL.&lt;/p&gt;

&lt;p&gt;This may be setup on a primary domain (which may introduce conflicts as new URLs are created), or this may be setup on a secondary domain (e.g. short domain) or subdomain to reduce the possibility of naming collisions.&lt;/p&gt;

&lt;p&gt;It may be desirable to prevent direct access to the redirect map file; this can be accomplished with a simple &lt;code class=&quot;highlighter-rouge&quot;&gt;RedirectRule&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Disable direct access to short URL map&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_URI} ^/shorturls\. [NC]
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; ^ - [R=404]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;faster-redirects-with-dbm-files&quot;&gt;Faster Redirects with DBM files&lt;/h1&gt;

&lt;p&gt;As the number of redirects grows, this solution may become slower (though the number of redirects would need to be very large). In any case, the &lt;code class=&quot;highlighter-rouge&quot;&gt;httxt2dbm&lt;/code&gt; command can be used to create &lt;code class=&quot;highlighter-rouge&quot;&gt;dbm&lt;/code&gt; files for use with &lt;code class=&quot;highlighter-rouge&quot;&gt;RewriteMap&lt;/code&gt;. A &lt;code class=&quot;highlighter-rouge&quot;&gt;dbm&lt;/code&gt; database file contains key/value pairs which map the redirect data; this works exactly the same way as a &lt;code class=&quot;highlighter-rouge&quot;&gt;txt&lt;/code&gt; map, but it is much faster, because the &lt;code class=&quot;highlighter-rouge&quot;&gt;dbm&lt;/code&gt; file is &lt;em&gt;indexed&lt;/em&gt;. Rather than processing each line of the file until a match is found (which happens for &lt;code class=&quot;highlighter-rouge&quot;&gt;txt&lt;/code&gt; files), the &lt;code class=&quot;highlighter-rouge&quot;&gt;dbm&lt;/code&gt; file allows Apache to jump right to the relevant entry. This can greatly improve performance for large data sets.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;httxt2dbm &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; shorturls.txt &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; shorturls.map
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;Note that with some dbm types, more than one file is generated, with a common base name. For example, you may have two files named mapfile.map.dir and mapfiile.map.pag. This is normal, and you need only use the base name mapfile.map in your RewriteMap directive.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://httpd.apache.org/docs/current/rewrite/rewritemap.html#dbm&quot;&gt;https://httpd.apache.org/docs/current/rewrite/rewritemap.html#dbm&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This command may be introduced into build logic, allowing the created &lt;code class=&quot;highlighter-rouge&quot;&gt;dbm&lt;/code&gt; file(s) to be created automatically whenever content changes.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Advanced nth-child Selectors</title>
   <link href="https://joeyhoer.com/advanced-nth-child-selectors-963fcac5"/>
   <updated>2017-10-05T10:56:51-04:00</updated>
   <id>https://joeyhoer.com/advanced-nth-child-selectors</id>
   <content type="html">&lt;p&gt;Frontend developers have long known that CSS &lt;code class=&quot;highlighter-rouge&quot;&gt;nth-child&lt;/code&gt; selectors may be combined to yield interesting and practical results. Most commonly, this technique is applied to select all items in a list when there are &lt;a href=&quot;https://alistapart.com/article/quantity-queries-for-css&quot;&gt;&lt;em&gt;more than N children&lt;/em&gt;, &lt;em&gt;less than N children&lt;/em&gt;&lt;/a&gt;, &lt;a href=&quot;https://grack.com/blog/2015/01/09/abusing-css3-selectors/&quot;&gt;&lt;em&gt;exactly N children&lt;/em&gt;&lt;/a&gt;, or &lt;a href=&quot;https://github.com/danielguillan/quantity-queries&quot;&gt;&lt;em&gt;between N and M children&lt;/em&gt;&lt;/a&gt;. However these simple selectors may also be combined in other ways to produce even more advanced selectors.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h1 id=&quot;modulo-selector&quot;&gt;Modulo Selector&lt;/h1&gt;

&lt;p&gt;The CSS &lt;strong&gt;modulo selector&lt;/strong&gt; can be used to select only the remaining items (quotient) after dividing a list of items by a number &lt;em&gt;n&lt;/em&gt;. This can be used in conjunction with a grid system to ensure that all rows are filled evenly by hiding extra elements. This is particularly useful for responsive designs in which the number of columns changes between viewports, yet the desire is to keep all rows filled evenly.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; I recommend hiding only unnecessary elements, and I discourage loading more content than necessary on any device, particularly on mobile devices.&lt;/p&gt;

&lt;p class=&quot;codepen&quot; data-embed-version=&quot;2&quot; data-theme-id=&quot;dark&quot; data-rerun-position=&quot;hidden&quot; data-slug-hash=&quot;yNwqeg&quot; data-height=&quot;400px&quot;&gt;
  See the Pen &lt;a href=&quot;//codepen.io/pen/yNwqeg&quot;&gt;CSS Modulo Quantity Query Selector&lt;/a&gt;
  by Joey Hoer (&lt;a href=&quot;//codepen.io/joeyhoer&quot;&gt;joeyhoer&lt;/a&gt;).
&lt;/p&gt;

&lt;p&gt;This technique is accomplished by selecting the final element in a complete group (i.e. full row) using &lt;code class=&quot;highlighter-rouge&quot;&gt;nth-child(an)&lt;/code&gt; (where &lt;code class=&quot;highlighter-rouge&quot;&gt;a&lt;/code&gt; is the divisor), and then counting backwards from the last element using &lt;code class=&quot;highlighter-rouge&quot;&gt;nth-last-child(b)&lt;/code&gt; (where &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; is a number between &lt;code class=&quot;highlighter-rouge&quot;&gt;2&lt;/code&gt; and the divisor) until that element is matched. Once the element is found, the general sibling combinator (&lt;code class=&quot;highlighter-rouge&quot;&gt;~&lt;/code&gt;) is used to select the remaining items in the list.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-css&quot; data-lang=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;/* Modulo 4 */&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;::nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;4n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;::nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;4n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*,&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;::nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;4n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;~&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; We begin with &lt;code class=&quot;highlighter-rouge&quot;&gt;2&lt;/code&gt; because &lt;code class=&quot;highlighter-rouge&quot;&gt;nth-last-child(1)&lt;/code&gt; will always be the &lt;code class=&quot;highlighter-rouge&quot;&gt;last-child&lt;/code&gt;, and when &lt;code class=&quot;highlighter-rouge&quot;&gt;nth-child(an)&lt;/code&gt; matches &lt;code class=&quot;highlighter-rouge&quot;&gt;last-child&lt;/code&gt;, there is no remainder.&lt;/p&gt;

&lt;p&gt;This approach works great except when &lt;code class=&quot;highlighter-rouge&quot;&gt;n&lt;/code&gt; is &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;, as there is then no element to match. This means that the first group is never selected. To overcome this challenge, an additional less-than quantity query may used to select items in an incomplete first group/row.&lt;/p&gt;

&lt;h1 id=&quot;middle-element-selector&quot;&gt;Middle Element Selector&lt;/h1&gt;

&lt;p&gt;The CSS &lt;strong&gt;middle element selector&lt;/strong&gt; is used to select the middle element in a list of elements. This is particularly useful when used with the general sibling combinator (&lt;code class=&quot;highlighter-rouge&quot;&gt;~&lt;/code&gt;) to select items in the second half of a list. This technique can be used add a different background to half of a list, and to anchor menus to the right rather than the left and prevent overflow on the right side of the screen.&lt;/p&gt;

&lt;p class=&quot;codepen&quot; data-embed-version=&quot;2&quot; data-theme-id=&quot;dark&quot; data-rerun-position=&quot;hidden&quot; data-slug-hash=&quot;LEbwGJ&quot; data-height=&quot;400px&quot;&gt;
  See the Pen &lt;a href=&quot;//codepen.io/pen/LEbwGJ&quot;&gt;CSS Middle Child Selector&lt;/a&gt;
  by Joey Hoer (&lt;a href=&quot;//codepen.io/joeyhoer&quot;&gt;joeyhoer&lt;/a&gt;).
&lt;/p&gt;

&lt;p&gt;Unfortunately, this technique produces a lot of generated code, and while the required code is very repetitive and therefore quite compressible, it is inelegant because it is difficult to maintain and scale because a maximum number of elements must be known in advance.&lt;/p&gt;

&lt;p&gt;This technique is accomplished using &lt;code class=&quot;highlighter-rouge&quot;&gt;:nth-child(b)&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;:nth-last-child(b)&lt;/code&gt; in equal measure by incrementing &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; from the start until the median is matched. Because this technique requires a unique selector for each distinct quantity, the total number of required selectors is &lt;em&gt;at least&lt;/em&gt; half of the maximum list size. If a list minimum is also known, the number of overall selectors required may be reduced further.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-css&quot; data-lang=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;/* Exact middle element of a list of 6–14 elements */&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Lists with an even count present an issue, as they do not have a median. In this case, the developer must choose either to select the preceding element, the following element, both elements or neither. Selecting both elements requires an additional CSS selector, effectively doubling the number of selectors required.&lt;/p&gt;

&lt;p&gt;These selectors may combined with the general sibling combinator (&lt;code class=&quot;highlighter-rouge&quot;&gt;~&lt;/code&gt;) to select the items in the second half of the list. Because the general sibling combinator is not inclusive (meaning that the current element is not selected) it will typically be desired to target the “after” method when selecting even numbered lists, and add an additional selector to make the list inclusive. This technique also doubles the number of selectors required.&lt;/p&gt;

&lt;p&gt;There is no way to select only the first half of a list using this method.&lt;/p&gt;

&lt;h1 id=&quot;half-of-elements-selector&quot;&gt;Half of Elements Selector&lt;/h1&gt;

&lt;p&gt;To work around the limitations of the middle element selector, the CSS &lt;strong&gt;half of elements selector&lt;/strong&gt; can select both the first half of a list, and the second half of a list using a fraction of the number of required selectors.&lt;/p&gt;

&lt;p class=&quot;codepen&quot; data-embed-version=&quot;2&quot; data-theme-id=&quot;dark&quot; data-rerun-position=&quot;hidden&quot; data-slug-hash=&quot;LzdKQN&quot; data-height=&quot;400px&quot;&gt;
  See the Pen &lt;a href=&quot;//codepen.io/pen/LzdKQN&quot;&gt;CSS Half Selector&lt;/a&gt;
  by Joey Hoer (&lt;a href=&quot;//codepen.io/joeyhoer&quot;&gt;joeyhoer&lt;/a&gt;).
&lt;/p&gt;

&lt;p&gt;This technique is accomplished using &lt;code class=&quot;highlighter-rouge&quot;&gt;:nth-child(-n+b)&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;:nth-last-child(n+b)&lt;/code&gt;, incrementing &lt;code class=&quot;highlighter-rouge&quot;&gt;b&lt;/code&gt; until the median is matched. The &lt;code class=&quot;highlighter-rouge&quot;&gt;n&lt;/code&gt; value overlaps either in a positive or negative direction from the median, based on the signs associated with the &lt;code class=&quot;highlighter-rouge&quot;&gt;nth-child&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;nth-last-child&lt;/code&gt; equations. Because this technique requires a unique selector for each distinct quantity, the total number of required selectors is &lt;em&gt;at least&lt;/em&gt; half of the maximum list size. Fortunately, this technique is also very repetitive and compressible.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-css&quot; data-lang=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;/* First half of elements in list of 1–14 elements */&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:nth-last-child&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Lists with an odd count present an issue, as they cannot be divided evenly. In this case, the developer must choose whether to select the median element; doing so does not increase the number of selectors required.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>The URL is Dead, Long Live the URL</title>
   <link href="https://joeyhoer.com/the-url-is-dead-long-live-the-url-8e87de87"/>
   <updated>2017-08-20T19:16:03-04:00</updated>
   <id>https://joeyhoer.com/the-url-is-dead-long-live-the-url</id>
   <content type="html">&lt;p&gt;I care about URLs. I have spent time ensuring the URLs for this site are clean, concise, uniform, readable, and easily sharable. I subscribe to the idea that &lt;a href=&quot;http://www.w3.org/Provider/Style/URI.html&quot;&gt;cool URIs don’t change&lt;/a&gt; and that &lt;a href=&quot;https://www.hanselman.com/blog/URLsAreUI.aspx&quot;&gt;URLs are UI&lt;/a&gt;. Even while I recognize that &lt;a href=&quot;http://www.brucelawson.co.uk/2016/on-urls-in-progressive-web-apps/&quot;&gt;the “appification” of the web has minimized the importance of URLs&lt;/a&gt;, and I expect URLs to become increasingly niche as computer interfaces become conversational, the URL remains &lt;em&gt;the&lt;/em&gt; defining feature of “the web” as we know it today, and I believe we should design URLs to embolden visitors with confidence.&lt;/p&gt;

&lt;!--more--&gt;

&lt;blockquote&gt;
  &lt;p&gt;The web has always been a nebulous concept, but at its center is the idea that everything can be linked.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://daringfireball.net/2014/04/rethinking_what_we_mean_by_mobile_web&quot;&gt;Rethinking What We Mean by ‘Mobile Web’&lt;/a&gt;, John Gruber&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;the-long-slow-death-of-the-url&quot;&gt;The Long, Slow Death of the URL&lt;/h1&gt;

&lt;blockquote&gt;
  &lt;p&gt;Anything that reduces the prevalence and usefulness of cross-site linking is a direct attack on the founding principle of the Web.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://www.nngroup.com/articles/fighting-linkrot/&quot;&gt;Fighting Linkrot&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In September 2013, iOS 7’s Mobile Safari began hiding all but the domain of visited URLs. Eight months later in April 2014, &lt;a href=&quot;https://jakearchibald.com/2014/improving-the-url-bar/&quot;&gt;the Chrome team&lt;/a&gt; experimented with &lt;a href=&quot;https://www.allenpike.com/2014/burying-the-url/&quot;&gt;burying the URL&lt;/a&gt;. The experiment was met with widespread &lt;a href=&quot;https://adactio.com/journal/6779/&quot;&gt;criticism&lt;/a&gt; from the development community—&lt;a href=&quot;https://news.ycombinator.com/item?id=7678580&quot;&gt;Paul Irish called it a “very bad change [which] runs anti-thetical to Chrome’s goals”&lt;/a&gt;—and &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=331373#c14&quot;&gt;the feature was “backburnered” a month later, and declared “dead” in 2015&lt;/a&gt;. Nevertheless, when OS 10.10 was released in October 2014, Apple introduced the “Smart Search” field to Safari 8 which brought the URL hiding features of iOS 7 to the Mac.&lt;/p&gt;

&lt;p&gt;Shortly after &lt;a href=&quot;https://infrequently.org/2015/06/progressive-apps-escaping-tabs-without-losing-our-soul/&quot;&gt;the advent of&lt;/a&gt; &lt;a href=&quot;https://developers.google.com/web/updates/2015/03/increasing-engagement-with-app-install-banners-in-chrome-for-android?hl=en&quot;&gt;installable PWAs&lt;/a&gt; &lt;a href=&quot;https://blog.chromium.org/2015/03/fchrome-42-beta-push-notifications_12.html&quot;&gt;in Chrome&lt;/a&gt; in 2015, the Chrome team launched &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse&quot;&gt;Lighthouse&lt;/a&gt;, a tool which checks whether a website meets Google’s criteria for PWA compatibility. The tool &lt;a href=&quot;https://github.com/samccone/lighthouse/blob/0295887b88eebf6d80146dd5b79ed6f018451d4b/lighthouse-core/audits/manifest-display.js#L30&quot;&gt;initially encouraged&lt;/a&gt; site owners to configure the site to “allow launching without address bar”—a recommendation which &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse/pull/496&quot;&gt;was eventually removed&lt;/a&gt; after &lt;a href=&quot;https://github.com/GoogleChrome/lighthouse/issues/495&quot;&gt;a lengthy discussion (once again involving Paul Irish) on GitHub&lt;/a&gt; and heated discussion &lt;a href=&quot;https://www.kryogenix.org/days/2016/05/24/the-importance-of-urls/&quot;&gt;around&lt;/a&gt; &lt;a href=&quot;https://adactio.com/journal/10708&quot;&gt;the web&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While that battle seems to have been won, for the time being, the URL fell under attack again when &lt;a href=&quot;https://googleblog.blogspot.com/2016/02/amping-up-in-mobile-search.html&quot;&gt;Google released AMP (Accelerated Mobile Pages)&lt;/a&gt; in February 2016. AMP content is cached on Google’s servers (to improve performance), and uses &lt;a href=&quot;https://developers.google.com/amp/cache/overview#amp-cache-url-format&quot;&gt;a non-standard URL format&lt;/a&gt;—once again, this garnered &lt;a href=&quot;https://80x24.net/post/the-problem-with-amp/&quot;&gt;widespread&lt;/a&gt; &lt;a href=&quot;https://daringfireball.net/linked/2017/01/17/schreiber-amp&quot;&gt;criticism&lt;/a&gt; from the &lt;a href=&quot;https://danielmiessler.com/blog/google-amp-not-good-thing/&quot;&gt;development&lt;/a&gt; &lt;a href=&quot;https://trib.tv/2017/03/31/amp-breaking-news/&quot;&gt;community&lt;/a&gt;. In February 2017, &lt;a href=&quot;https://developers.googleblog.com/2017/02/whats-in-amp-url.html&quot;&gt;Google updated AMP&lt;/a&gt; to allow users “to access, copy, and share the canonical URL of an AMP document … [via] an anchor button.” The experience remains suboptimal however, and has been &lt;a href=&quot;https://daringfireball.net/linked/2017/02/07/sharing-amp-urls&quot;&gt;dubbed by John Gruber as &lt;em&gt;begrudging UI&lt;/em&gt;&lt;/a&gt;. &lt;a href=&quot;https://twitter.com/viticci/status/900396684844433409&quot;&gt;iOS 11 may work around this issue; in iOS 11, when AMP pages are shared via the built-in share sheet, the canonical URL will be shared instead of the AMP URL&lt;/a&gt;. &lt;a href=&quot;https://news.ycombinator.com/item?id=15085159#unv_15086192&quot;&gt;According to an AMP Tech Lead&lt;/a&gt;, Google “specifically requested Apple (and other browser vendors) to do this … [as] AMP’s policy states that platforms should share the canonical URL of an article whenever technically possible.”&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The killer feature of the web—URLs—are being treated as something undesirable because they aren’t part of native apps. That’s not a failure of the web; that’s a failure of native apps.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://adactio.com/journal/10708&quot;&gt;Regressive Web Apps&lt;/a&gt;, Jeremy Keith&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Anyone witnessing this must ask themselves: “Why are we, the development community, so passionately protective over URLs? And why are these large internet companies so seemingly hellbent on devaluing them?”&lt;/p&gt;

&lt;p&gt;The answer lies in &lt;strong&gt;conversational UI&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;defending-the-url&quot;&gt;Defending the URL&lt;/h2&gt;

&lt;p&gt;Those who defend the URL do so because URLs are &lt;em&gt;universal&lt;/em&gt;. URLs work in every conceivable browser, on every conceivable device, in command line utilities without any UI at all, on billboards and business cards … URLs are extremely portable.&lt;/p&gt;

&lt;p&gt;The portability of the URL is part of what has made it such an important social tool. In 2012, &lt;a href=&quot;https://www.theatlantic.com/technology/archive/2012/10/dark-social-we-have-the-whole-history-of-the-web-wrong/263523/&quot;&gt;the Atlantic estimated&lt;/a&gt; that 56.5 percent of their social referrals came from “Dark Social” (i.e. direct URL sharing). At the same time, Chartbeat told the Atlantic that direct URL shares accounted for 69 percent of overall social referrals on sites that Chartbeat managed. More recent &lt;a href=&quot;https://radiumone.com/darksocial/&quot;&gt;reports from RadiumOne&lt;/a&gt; suggests Dark Social may now account for as much as 84 percent of social referrals.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The URL and the ability for anyone to mint a new one and then propagate it is what makes the web so resilient, so empowering, and so interesting! That I don’t need to ask anyone permission to create a new website or webpage is a kind of ideological freedom that few generations in history have known!&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;http://web.archive.org/web/20150910200713/http://factoryjoe.com/blog/2009/11/16/the-death-of-the-url/&quot;&gt;The death of the URL&lt;/a&gt;, Chris Messina&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;URLs are powerful tools that work everywhere. &lt;em&gt;Except …&lt;/em&gt; when it comes to &lt;em&gt;voice&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I’m sure you hear URLs on the radio. You may have even used them in a conversation, but ultimately anything beyond a domain name becomes incredibly difficult to communicate audibly. Those who look forward to conversational UI will argue that URLs don’t actually work on “every conceivable device” but only in text-based mediums.&lt;/p&gt;

&lt;h2 id=&quot;conversational-ui&quot;&gt;Conversational UI&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.nngroup.com/articles/url-as-ui/&quot;&gt;Jakob Nielsen predicted that speech could be the death of the URL in &lt;em&gt;URL as UI&lt;/em&gt; in 1999&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Domain names may die … In the long term, it is not appropriate to require unique words to identify every single entity in the world. &lt;strong&gt;That’s not how human language works.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I look at virtual “personal assistants” like Amazon’s Alexa, Google Assistant, Apple’s Siri, Microsoft’s Cortana, and Samsung’s Viv, it’s easy to see why the URL it starting to be treated like a second class citizen. Conversational UI will become a &lt;em&gt;huge&lt;/em&gt; part of human life in the not-so-distant future, and that is the true villain of the URL. The awesome and terrifying power of conversational UI is that data is &lt;strong&gt;aggregated, stripped of sources, and regurgitated as fact&lt;/strong&gt;. Speed is valued over clarity. The reckless devaluation of the URL is the first step towards a future, where queries are spoken and responses can truly be accessed &lt;em&gt;anywhere, anyhow&lt;/em&gt; … and (if we are not careful) where fiction becomes fact.&lt;/p&gt;

&lt;blockquote class=&quot;twitter-video&quot; data-status=&quot;hidden&quot;&gt;
  &lt;p&gt;And here's what happens if you ask Google Home &quot;is Obama planning a coup?&quot; &lt;a href=&quot;https://t.co/MzmZqGOOal&quot;&gt;pic.twitter.com/MzmZqGOOal&lt;/a&gt;&lt;/p&gt;
  — Rory Cellan-Jones (@ruskin147) &lt;a href=&quot;https://twitter.com/ruskin147/status/838445095410106368&quot;&gt;March 5, 2017&lt;/a&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Our ability to understand who we’re talking to on the web is underpinned by search engines.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://medium.com/@owencm/rethinking-url-bars-as-primary-browser-ui-e2118339d2c0&quot;&gt;Rethinking URL bars as primary browser UI&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Why is Google leading this charge? Because they are the only one’s who can. The cataloging and indexation of data has always been Google’s domain. Now, with forays into AI and neural networking, Google is in a position to turn the web into a closed API for clean, aggregate data.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Isn’t [Google] trying to guarantee the open Web stays alive? Not necessarily. [Google]’s goal is to gather as much rich data as possible, and build AI. Their mission is to have an AI provide timely and personalized information to us, not specifically to have websites provide information. Any GOOG concerted efforts are aligned to the AI mission.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://staltz.com/the-web-began-dying-in-2014-heres-how.html&quot;&gt;The Web Began Dying in 2014, Here’s How&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We see examples of this in &lt;a href=&quot;https://support.google.com/webmasters/answer/6229325?hl=en&quot;&gt;Google’s “Featured Snippets”&lt;/a&gt;, which have been referred to as Google’s &lt;a href=&quot;http://searchengineland.com/googles-one-true-answer-problem-featured-snippets-270549&quot;&gt;“One True Answer”&lt;/a&gt;, &lt;a href=&quot;http://www.marketingchamplu.com/google-featured-snippets-direct-answers-taking-traffic-from-webmasters/&quot;&gt;“Direct Answer”&lt;/a&gt; and &lt;a href=&quot;https://moz.com/blog/ranking-zero-seo-for-answers&quot;&gt;“Rank #0”&lt;/a&gt;. Featured Snippets scrape content from websites and display only the relevant data directly at the top of Google’s search results and &lt;a href=&quot;http://searchengineland.com/google-home-responding-featured-snippets-260473&quot;&gt;power Google’s Assistant&lt;/a&gt;. Some argue that this eliminates the need to visit the originating page. While the URL is displayed, the value of the link is diminished, the content has already been delivered, the “knowledge” gained. In September 2017, &lt;a href=&quot;http://searchengineland.com/google-officially-adds-amp-based-featured-snippets-mobile-search-results-282005&quot;&gt;Google confirmed that AMP links may be displayed in featured snippets of  mobile search results&lt;/a&gt;, providing searchers even less incentive to visit the originating website. The heart of the matter is &lt;a href=&quot;https://adactio.com/journal/13035&quot;&gt;can we trust Google?&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A big corporation (in [this] case Google) decides what is good for [us] and what [we] can find … and buy and use.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;http://michaelbumann.com/post/136530271067/the-death-of-the-url&quot;&gt;The death of the URL&lt;/a&gt;, Michael Bumann&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;By removing our ability to navigate, choose, and share freely … [these companies are] exchanging our freedom for a &lt;em&gt;promise&lt;/em&gt; that they’ll keep us safe, give us everything we need, and do all the choosing of what’s “good enough” for us …&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;http://web.archive.org/web/20150910200713/http://factoryjoe.com/blog/2009/11/16/the-death-of-the-url/&quot;&gt;The death of the URL&lt;/a&gt;, Chris Messina&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;placeholder screenshot-window&quot; style=&quot;--aspect-ratio:calc(100%/(806/426))&quot;&gt;
  &lt;div style=&quot;max-width:806px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Screenshot of a Google Featured Snippet&quot; width=&quot;806&quot; height=&quot;426&quot; integrity=&quot;sha256-CXXKzTqiyLX+LaYeMXTgHJKhbj0aUMwKEAEpDs/1Un8=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/google-featured-snippet-0975cacd3aa2c8b5fe2da61e3174e01c92a16e3d1a50cc0a1001290ecff5527f.png&quot; /&gt;
&lt;/div&gt;

&lt;div class=&quot;note&quot;&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;Well “You can’t blame me”, says the media man&lt;br /&gt;
… I just point my camera at what the people want to see&lt;br /&gt;
Man it’s a two way mirror and you can’t blame me”&lt;/em&gt;&lt;/p&gt;

  &lt;p&gt;– &lt;em&gt;Cookie Jar&lt;/em&gt; by Jack Johnson&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fortunately, &lt;a href=&quot;https://www.blog.google/products/search/our-latest-quality-improvements-search/&quot;&gt;Google &lt;em&gt;is&lt;/em&gt; taking responsibility and actively trying to cleanup Featured Snippets&lt;/a&gt;. As a consumer, I admire Google’s initiative. I appreciate getting answers at the touch of a button, at the tip of the tongue, without having to pour over pages of content, links, and advertising just to get at the good bits. It saves me valuable time, and on the web, time is capital. At Google, content is cheap.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;URLs &lt;em&gt;are&lt;/em&gt; the web. They are critical to assessing credibility in the digital age.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Microsoft Research conducted an &lt;a href=&quot;http://research.microsoft.com/apps/pubs/default.aspx?id=70395&quot;&gt;eyetracking study of search engine use&lt;/a&gt; that found that people spend 24% of their gaze time looking at the URLs in the search results.&lt;/p&gt;

  &lt;p&gt;We found that searchers are particularly interested in the URL when &lt;strong&gt;they are assessing the credibility of a destination&lt;/strong&gt;. If the URL looks like garbage, people are less likely to click …&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://www.nngroup.com/articles/url-as-ui/&quot;&gt;URL as UI&lt;/a&gt;, Jakob Nielsen&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The war on the URL is far from over. If we want the URL to live on, it is our responsibility, as web practitioners, to design URLs that are easily spoken and easily remembered.&lt;/p&gt;

&lt;h1 id=&quot;designing-urls-for-humans&quot;&gt;Designing URLs for Humans&lt;/h1&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; This section contains technical details of how to implement URL designs in &lt;em&gt;Apache 2.4&lt;/em&gt; ; be aware of your server type and version, as they may be different.&lt;/p&gt;

&lt;p&gt;URLs are prime real estate. Invest in your URLs. Make URLs a priority.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;URLs are for humans. Design them for humans.&lt;/p&gt;

  &lt;p&gt;Any regular semi-technical user of your site should be able to navigate 90% of your app based off memory of the URL structure. In order to achieve this, your URLs will need to be &lt;em&gt;pragmatic&lt;/em&gt;.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;http://warpspire.com/posts/url-design&quot;&gt;URL Design&lt;/a&gt;, Kyle Neath&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Designing URLs mostly means leaving information out. When designing URLs there are two basic elements to keep in mind: &lt;em&gt;simplicity&lt;/em&gt; and &lt;em&gt;consistency&lt;/em&gt;. These basic elements can be applied to each of the following rules for URL design. URLs should be:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;short&lt;/li&gt;
  &lt;li&gt;easy-to-type&lt;/li&gt;
  &lt;li&gt;hierarchical (visualize the site structure)&lt;/li&gt;
  &lt;li&gt;malleable (enable navigation by removing segments of the URL)&lt;/li&gt;
  &lt;li&gt;persistent&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote class=&quot;twitter-tweet&quot; data-conversation=&quot;none&quot;&gt;
  &lt;p&gt;I want people to be able to copy URLs. I want people to be able to hack URLs. I'm not ashamed of my URLs …I'm downright proud.&lt;/p&gt;
  — Jeremy Keith (@adactio) &lt;a href=&quot;https://twitter.com/adactio/status/734875747169501185&quot;&gt;May 23, 2016&lt;/a&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;url-length&quot;&gt;URL Length&lt;/h2&gt;

&lt;p&gt;The full, indexed URL (including the scheme and host) should be &lt;strong&gt;between 75–115 characters in length&lt;/strong&gt; to allow the full URL to be displayed in SERPs and preserve the readability and shareability of links.&lt;/p&gt;

&lt;p&gt;In 2007, &lt;a href=&quot;https://www.marketingsherpa.com/article/blog/shorter-urls-equal-250-more&quot;&gt;a MarketingSherpa study&lt;/a&gt; found that shorter URLs were 250% more likely to be clicked in search results when directly compared with longer URLs.&lt;/p&gt;

&lt;h2 id=&quot;url-simplicity&quot;&gt;URL Simplicity&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use only lowercase letters, digits and hyphens&lt;/strong&gt; (i.e. dashes, &lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt;) in URLs.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Use short, full, and commonly known words. If a path segment has a dash or special character in it, the word is probably too long.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;http://warpspire.com/posts/url-design&quot;&gt;URL Design&lt;/a&gt;, Kyle Neath&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;hyphens-vs-underscores&quot;&gt;Hyphens vs Underscores&lt;/h3&gt;

&lt;p&gt;While they are technically permitted as “unreserved characters” in URIs, &lt;strong&gt;do not use tildes or underscores (i.e. &lt;code class=&quot;highlighter-rouge&quot;&gt;~&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;_&lt;/code&gt;) in URLs&lt;/strong&gt;, and use periods only when file extensions are required.&lt;/p&gt;

&lt;p&gt;Hyphens &lt;em&gt;feel&lt;/em&gt; native in the context of URLs as they are allowed in &lt;a href=&quot;https://tools.ietf.org/html/rfc1123#section-2&quot;&gt;hostnames&lt;/a&gt; and &lt;a href=&quot;https://tools.ietf.org/html/rfc1035#section-2.3.1&quot;&gt;domain names&lt;/a&gt;, while other characters (such as underscores) are not. Additionally, hyphens are easier to type—no need to use the
&lt;kbd class=&quot;combo&quot;&gt;
  &lt;kbd&gt;&lt;span&gt;&lt;abbr title=&quot;Shift&quot;&gt;⇧&lt;/abbr&gt;&lt;/span&gt;&lt;/kbd&gt;
&lt;/kbd&gt;
key—and hyphens are not obscured by underlines. Also, &lt;a href=&quot;https://support.google.com/webmasters/answer/76329?hl=en&quot;&gt;Google recommends using hyphens instead of underscores&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While &lt;a href=&quot;https://panic.com/blog/the-worlds-first-emoji-domain/&quot;&gt;emoji domains&lt;/a&gt; are cool (I own a few myself: &lt;a href=&quot;https://🌩.ws&quot;&gt;🌩.ws&lt;/a&gt; and &lt;a href=&quot;https://☄.ws&quot;&gt;☄.ws&lt;/a&gt;), non-ASCII characters are difficult to type and &lt;a href=&quot;https://www.аррӏе.com&quot;&gt;sometimes difficult to differentiate&lt;/a&gt;. If you are targeting an English speaking audience, &lt;strong&gt;use only &lt;a href=&quot;https://tools.ietf.org/html/rfc3986#section-2.3&quot;&gt;unreserved ASCII characters&lt;/a&gt; in URLs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When using a post or product title (page subject) in the URL, replace spaces and special characters with hyphens. Typically apostrophes and quotes may be removed altogether. Multiple sequential hyphens should be collapsed to a single hyphen.&lt;/p&gt;

&lt;h3 id=&quot;lowercase-urls&quot;&gt;Lowercase URLs&lt;/h3&gt;

&lt;p&gt;Browsers and search engines treat each unique URL as if it contains unique content, because it is technically possible, through web server configuration, to serve &lt;em&gt;different&lt;/em&gt; content at each distinct URL. If distinct URLs are not appropriately redirected or canonicalized, browser caching, edge caching and search engine results may be impacted. As best practice, &lt;a href=&quot;/canonical-links-a0f1b14f#canonicalization-vs-redirection&quot;&gt;redirection should be preferred over canonicalization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While URLs themselves are case sensitive (to support file systems which are case sensitive [e.g UNIX]), &lt;a href=&quot;https://tools.ietf.org/html/rfc952&quot;&gt;domain names and hostnames&lt;/a&gt; make no distinction between upper and lower case. &lt;strong&gt;Use only lowercase letters&lt;/strong&gt; in URLs to standardize their appearance and avoid technical issues.&lt;/p&gt;

&lt;p&gt;To ensure that all URLs are appropriately redirected to their lowercase equivalent, include the following &lt;a href=&quot;https://httpd.apache.org/docs/2.4/rewrite/rewritemap.html&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;RewriteMap&lt;/code&gt;&lt;/a&gt; in your site’s Apache configuration files (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;httpd.conf&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;vhosts.conf&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Add map to force lowercase URLs&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteMap&lt;/span&gt; lowercase int:tolower
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, in your &lt;code class=&quot;highlighter-rouge&quot;&gt;.htaccess&lt;/code&gt; file, add the following redirect:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Lowercase URLs&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Skip case sensitive files/directories manually&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Skip downloads directory&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_URI} !^/downloads/
&lt;span class=&quot;c&quot;&gt;# Skip dotfiles&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Important for letsencrypt challenge authentication&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_URI} !/\.
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; $1 [A-Z]
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; (.*) ${lowercase:%{REQUEST_URI}} [R=301,L]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Apache’s &lt;a href=&quot;http://httpd.apache.org/docs/current/mod/mod_speling.html&quot;&gt;mod_speling&lt;/a&gt; may also be used to resolve this issue by letting the server decide which file to serve.&lt;/p&gt;

&lt;h3 id=&quot;remove-file-extensions&quot;&gt;Remove File Extensions&lt;/h3&gt;

&lt;p&gt;URLs should be independent of implementation. File extensions generally denote the underlying technology powering a website—information which is irrelevant to end users.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; Files which are intended to be &lt;em&gt;downloaded&lt;/em&gt; typically &lt;strong&gt;should&lt;/strong&gt; include an extension.&lt;/p&gt;

&lt;p&gt;One benefit of using extensionless URLs is that if you decide to migrate from one language to another (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;ASP&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&gt;PHP&lt;/code&gt;), the URLs will not need to be changed or redirected (which always results in a slight SEO penalty and dip in organic traffic).&lt;/p&gt;

&lt;p&gt;To enable Apache to serve extensionless URLs, add the following &lt;code class=&quot;highlighter-rouge&quot;&gt;RedirectRule&lt;/code&gt; to your &lt;code class=&quot;highlighter-rouge&quot;&gt;.htaccess&lt;/code&gt; file:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Support extensionless paths&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_FILENAME} !-f
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_FILENAME} !-d
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_FILENAME} !-l
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; ^([^\.]+[^/])/*$ $1.html [L]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this setup, the files will be available both at the extensioned path (with the &lt;code class=&quot;highlighter-rouge&quot;&gt;.html&lt;/code&gt;) and without. If you do not canonicalize these files, search engines may flag these distinct URLs as duplicate content.&lt;/p&gt;

&lt;p&gt;To resolve this, redirect or canonicalize the URLs, or disable access to the extensioned URL altogether:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Disable direct access to files with extensions&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_URI} \.(p?html?|php)$ [NC,OR]
&lt;span class=&quot;c&quot;&gt;# Disable direct access to index files&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_URI} /index$ [NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{ENV:REDIRECT_STATUS} ^$
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; ^ - [R=404]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Another solution is to enable &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation&quot;&gt;content negotiation&lt;/a&gt; in your web server. Your web server &lt;a href=&quot;http://httpd.apache.org/docs/current/content-negotiation.html&quot;&gt;will attempt to serve the correct file type&lt;/a&gt;, based on &lt;code class=&quot;highlighter-rouge&quot;&gt;Accept&lt;/code&gt; headers provided in the request and naming conventions followed on the server.&lt;/p&gt;

&lt;h3 id=&quot;trailing-slash&quot;&gt;Trailing Slash&lt;/h3&gt;

&lt;p&gt;Adding a trailing slash to URLs is largely a matter of preference; there is no distinct technical or &lt;a href=&quot;https://webmasters.googleblog.com/2010/04/to-slash-or-not-to-slash.html&quot;&gt;SEO&lt;/a&gt; advantage to trailing slashes. &lt;strong&gt;Be consistent.&lt;/strong&gt; Avoid mixing URLs with trailing slashes and non-trailing slashes on your site, and be sure to redirect or canonicalize URLs appropriately.&lt;/p&gt;

&lt;p&gt;Trailing slashes present some important technical considerations for relative URLs. For example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;With Trailing Slash:&lt;/strong&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;resource&lt;/code&gt; relative to &lt;code class=&quot;highlighter-rouge&quot;&gt;/location/&lt;/code&gt; is &lt;code class=&quot;highlighter-rouge&quot;&gt;/location/resource&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Without Trailing Slash:&lt;/strong&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;resource&lt;/code&gt; relative to &lt;code class=&quot;highlighter-rouge&quot;&gt;/location&lt;/code&gt; is &lt;code class=&quot;highlighter-rouge&quot;&gt;/resource&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This behavior mirrors UNIX file systems in which:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Slashes denote directories (as &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt; is a directory separator)&lt;/li&gt;
  &lt;li&gt;File names do not require a file extension, but &lt;a href=&quot;http://www.linfo.org/file_name.html&quot;&gt;cannot contain slashes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using a trailing slash may make some content more portable, easier to organize, and could save some characters when linking to content via relative URLs. However, a site/page would need to be architected very carefully to take advantage of these benefits.&lt;/p&gt;

&lt;p&gt;The question you must ask yourself is: should “directories” always include a trailing slash, or should path segments only include the slashes between hierarchical ancestors? For example, if we navigate upwards from the URL &lt;code class=&quot;highlighter-rouge&quot;&gt;http://example.com/collection/item&lt;/code&gt;, which of the following URLs is cleaner?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;http://example.com/collection&lt;/li&gt;
  &lt;li&gt;http://example.com/collection/&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essentially, a trailing slash indicates that a URL is part of &lt;a href=&quot;https://tools.ietf.org/html/rfc1738#section-2.3&quot;&gt;a hierarchical data structure&lt;/a&gt;, meaning the URL contains children &lt;em&gt;or may contain children in the future&lt;/em&gt;. If “directories” should always be labeled as such, with a trailing slash, then URLs should probably include the trailing slash, so that URLs will not need to change as a site expands.&lt;/p&gt;

&lt;p&gt;Keep in mind, bare domain names are &lt;em&gt;supposed&lt;/em&gt; to include a trailing slash, as according to &lt;a href=&quot;https://tools.ietf.org/html/rfc2616#section-5.1.2&quot;&gt;RFC 2616 §5.1.2&lt;/a&gt;, “if [no absolute path] is present in the original URI, it &lt;strong&gt;must&lt;/strong&gt; be given as &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt;”&lt;/p&gt;

&lt;p&gt;You can confirm this by &lt;code class=&quot;highlighter-rouge&quot;&gt;curl&lt;/code&gt;ing a bare domain name:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-sILo&lt;/span&gt; /dev/null &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; %&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;url_effective&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; google.com
http://www.google.com/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, modern browsers strip the slash from bare domain names.&lt;/p&gt;

&lt;div class=&quot;placeholder screenshot-window&quot; style=&quot;--aspect-ratio:calc(100%/(592/74))&quot;&gt;
  &lt;div style=&quot;max-width:592px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Screenshot of bare domain name with trailing slash omitted in Google Chrome&quot; width=&quot;592&quot; height=&quot;74&quot; integrity=&quot;sha256-ULA3mzrcxogLU/4WBP/8T8BfTmIxP7WCubAk2Y0wVg4=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/chrome-bare-domain-name-50b0379b3adcc6880b53fe1604fffc4fc05f4e62313fb582b9b024d98d30560e.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;And yet, they still appear in Google search results…&lt;/p&gt;

&lt;div class=&quot;placeholder screenshot-window&quot; style=&quot;--aspect-ratio:calc(100%/(638/110))&quot;&gt;
  &lt;div style=&quot;max-width:638px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Screenshot of bare domain name in Google search results&quot; width=&quot;638&quot; height=&quot;110&quot; integrity=&quot;sha256-NTFx9K02rjZM2hNpgWdIk/Z9KvGfVAc/dxQe5yrRhnE=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/serp-bare-domain-name-353171f4ad36ae364cda136981674893f67d2af19f54073f77141ee72ad18671.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;At the time of writing, &lt;a href=&quot;https://www.google.com&quot;&gt;Google&lt;/a&gt;, &lt;a href=&quot;https://www.wikipedia.org&quot;&gt;Wikipedia&lt;/a&gt;, &lt;a href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;, &lt;a href=&quot;https://stackoverflow.com&quot;&gt;Stack Overflow&lt;/a&gt;, &lt;a href=&quot;https://twitter.com&quot;&gt;Twitter&lt;/a&gt; and &lt;a href=&quot;https://medium.com&quot;&gt;Medium&lt;/a&gt; omit the trailing slash, while &lt;a href=&quot;https://www.facebook.com&quot;&gt;Facebook&lt;/a&gt;, &lt;a href=&quot;https://wordpress.org&quot;&gt;WordPress&lt;/a&gt;,&lt;a href=&quot;https://www.reddit.com&quot;&gt;reddit&lt;/a&gt; and &lt;a href=&quot;https://www.apple.com&quot;&gt;Apple&lt;/a&gt; include the trailing slash.&lt;/p&gt;

&lt;p&gt;A subjective opinion is that a trailing slash on a URL is like a period at the end of a sentence—it balances the URL and provides closure. On the other hand, omitting the trailing slash is one less character to type.&lt;/p&gt;

&lt;p&gt;Because neither solution is objectively superior, I have included Apache rewrites for both solutions below.&lt;/p&gt;

&lt;p&gt;To add trailing slashes to URLs:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Add trailing slashes to URLs&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Ignore files with extensions&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_FILENAME} !-f
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_FILENAME} !-d
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_FILENAME} !-l
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_URI} !(\..*|/)$
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; .* $0/ [R=301]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To remove trailing slashes from URLs:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Remove trailing slashes from URLs&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_FILENAME} !-f
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_FILENAME} !-d
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_FILENAME} !-l
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; ^(.*)/+$ $1 [R=301]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;collapse-multiple-slashes&quot;&gt;Collapse Multiple Slashes&lt;/h3&gt;

&lt;p&gt;In most web server implementations, URIs map to an on disk path (this can vary depending upon configuration). Because modern operating systems do not assign any special meaning to sequential path separators (i.e. &lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt;), paths which include multiple path separators (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;/path/to/file&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;/path//to///file&lt;/code&gt;) map to the same file. Therefore the following URLs (which are valid according to &lt;a href=&quot;https://tools.ietf.org/html/rfc3986#section-3.3&quot;&gt;RFC3986 §3.3&lt;/a&gt;) map to the same location by default:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;https://example.com/path/to/file&lt;/li&gt;
  &lt;li&gt;https://example.com/path//to///file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While the same content may be served at both URLs, browsers will not munge the URL by automatically collapsing multiple slashes. This is because it is possible, through web server configuration, to serve &lt;em&gt;different&lt;/em&gt; content at both URLs. Browsers and search engines must account for these potential differences and treat each unique URL as unique content unless they are explicitly told not to do so. Because of this, multiple slashes in URLs may affect browser caching and edge caching as well as search engine results if the URL is not appropriately redirected or labeled with a relevant canonical URL.&lt;/p&gt;

&lt;p&gt;Multiple slashes also present issues with relative URLs. In relative URLs, “dot-segments” are used to traverse “the URI path hierarchy” (e.g. the &lt;code class=&quot;highlighter-rouge&quot;&gt;..&lt;/code&gt; path segment moves up one “level” in the path hierarchy—&lt;a href=&quot;http://www.linfo.org/dot.html&quot;&gt;equivalent to “parent directory” in the filesystem&lt;/a&gt;). If &lt;a href=&quot;https://www.w3.org/TR/html5/document-metadata#the-base-element&quot;&gt;the &lt;code class=&quot;highlighter-rouge&quot;&gt;base&lt;/code&gt; element&lt;/a&gt; is not correctly defined on a page, multiple path separators will affect the ability to properly navigate the path hierarchy with relative URLs.&lt;/p&gt;

&lt;p&gt;As such, it is best practice to &lt;strong&gt;collapse multiple slashes in URLs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In Apache, multiple slashes may be collapsed with the following &lt;code class=&quot;highlighter-rouge&quot;&gt;RewriteRule&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Collapse multiple slashes in URLs&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{THE_REQUEST} //
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; .* /$0 [R=301,NE]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;stripping-url-parameters-query-string&quot;&gt;Stripping URL Parameters (Query String)&lt;/h3&gt;

&lt;p&gt;Many third party services (like email providers, social sites and affiliates)  add query parameters onto URLs shared through their platform or service, so their service will be identified in your site’s analytics, and (assuming you’ve installed the corresponding JavaScript “pixel”) so they can track your traffic themselves.&lt;/p&gt;

&lt;p&gt;While this benefits site owners and affiliates, these URLs sacrifice user experience. These additional parameters lengthen the URL, making it difficult to read and unpleasant to share. These parameters may also be inadvertently shared when visitors copy and paste URLs, which can skew analytics data.&lt;/p&gt;

&lt;p&gt;Even if you do not directly advertise your site through such services, because of the nature of the web, anyone can link to your site through one of these services and these parameters will be displayed to your visitors.&lt;/p&gt;

&lt;p&gt;Help users understand the content of the current view by &lt;strong&gt;removing unnecessary query parameters from URLs&lt;/strong&gt;.&lt;/p&gt;

&lt;div&gt;
  &lt;video autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot; class=&quot;screenshot-window&quot; style=&quot;border-radius: 5px 5px 0px 0px&quot;&gt;
    &lt;source src=&quot;/assets/strip-query-string-782c2c17539d073af7e57702ff1c9edd9d57ac0b35b43a27bb22b487fdf3b666.mp4&quot; type=&quot;video/mp4&quot; /&gt;
  &lt;/video&gt;
&lt;/div&gt;

&lt;p&gt;One method to scrub query strings from URLs is to use your web server to redirect to the same URL without the query string:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Remove query string from the URL&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Note: In Apache 2.4 or later, the &quot;QSD&quot; option (qsdiscard)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# can be used to remove the query string opposed to&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# appending a &quot;?&quot; the the substitution string&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} .
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; (.*) /$1? [R=301,L]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The disadvantage of redirecting at the server level is that once the page has been redirected, the query string is no longer available to JavaScript based analytics tools (like Google Analytics). Ideally, you would clean the URL &lt;em&gt;and&lt;/em&gt; track the data correctly in analytics tools.&lt;/p&gt;

&lt;p&gt;A better solution is to strip the query string from the URL once the page has loaded and a pageview has been tracked. This is possible using JavaScript’s &lt;code class=&quot;highlighter-rouge&quot;&gt;history&lt;/code&gt; API.&lt;/p&gt;

&lt;div class=&quot;language-js highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-js&quot; data-lang=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Shareable URLs&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Strip query parameters from the URL&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;){&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;history&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;replaceState&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({},&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;pathname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;l&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;hash&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)})(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;location&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This removes the entire query string. Sometimes &lt;em&gt;some&lt;/em&gt; URL parameters will be required for rendering a unique view; in that case, you will need a solution that allows these necessary query parameters to be whitelisted. This can be accomplished with a more advanced JavaScript solution like &lt;a href=&quot;https://github.com/wistia/fresh-url&quot;&gt;Fresh URL&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;redirect-to-secure-www&quot;&gt;Redirect to secure www&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use &lt;code class=&quot;highlighter-rouge&quot;&gt;https&lt;/code&gt;&lt;/strong&gt; for the &lt;a href=&quot;https://webmasters.googleblog.com/2014/08/https-as-ranking-signal.html&quot;&gt;SEO benefits&lt;/a&gt;; it’s just &lt;a href=&quot;https://letsencrypt.org&quot;&gt;so easy&lt;/a&gt; that there is really no excuse.&lt;/p&gt;

&lt;p&gt;In 2015, &lt;a href=&quot;https://security.googleblog.com/2015/10/simplifying-page-security-icon-in-chrome.html&quot;&gt;Chrome 46 introduced changes to mixed content warnings in the address bar&lt;/a&gt; to “encourage site operators to switch to HTTPS sooner rather than later”, also announcing a plan to eventually label all HTTP pages as non-secure. In 2016, &lt;a href=&quot;https://security.googleblog.com/2016/09/moving-towards-more-secure-web.html&quot;&gt;Chrome 56 began labeling HTTP pages with password and credit card fields as “not secure”&lt;/a&gt; and reiterated the intent to move toward two states, secure and not secure, and has been &lt;a href=&quot;https://security.googleblog.com/2017/04/next-steps-toward-more-connection.html&quot;&gt;marching steadily towards that goal&lt;/a&gt;. &lt;a href=&quot;https://developers.google.com/web/updates/2016/10/avoid-not-secure-warn#long_term_-_use_https_everywhere&quot;&gt;Google recommends using “HTTPS everywhere”&lt;/a&gt; as a long term solution. In 2018, &lt;a href=&quot;https://security.googleblog.com/2018/02/a-secure-web-is-here-to-stay.html&quot;&gt;with the release of Chrome 68&lt;/a&gt;, Chrome began marking all HTTP sites as “not secure”.&lt;/p&gt;

&lt;p&gt;Additionally, &lt;code class=&quot;highlighter-rouge&quot;&gt;http2&lt;/code&gt; (which introduces performance benefits) is &lt;a href=&quot;https://http2.github.io/faq/#does-http2-require-encryption&quot;&gt;currently supported only over HTTPS&lt;/a&gt; and &lt;a href=&quot;https://www.w3.org/TR/secure-contexts/&quot;&gt;“powerful features” of HTML5&lt;/a&gt; require HTTPS. These features include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Geolocation&lt;/li&gt;
  &lt;li&gt;Video and Microphone&lt;/li&gt;
  &lt;li&gt;Device motion / orientation&lt;/li&gt;
  &lt;li&gt;Fullscreen&lt;/li&gt;
  &lt;li&gt;Pointer locking&lt;/li&gt;
  &lt;li&gt;Service Workers&lt;/li&gt;
  &lt;li&gt;AppCache&lt;/li&gt;
  &lt;li&gt;Notifications&lt;/li&gt;
  &lt;li&gt;Web Bluetooth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not using HTTPS can also impact your site’s analytics data. When a visitor arrives on an HTTP site from an HTTPS referrer via a link, &lt;a href=&quot;https://scotthelme.co.uk/a-new-security-header-referrer-policy/&quot;&gt;the referrer is stripped&lt;/a&gt;; this traffic is labeled “dark” traffic (i.e. visits for which the origin cannot be determined) which Google Analytics records as “direct” acquisition. In contrast, traveling from an HTTP or HTTPS site &lt;em&gt;to&lt;/em&gt; an HTTPS site &lt;em&gt;will&lt;/em&gt; properly attribute the referral data in Google Analytics. While it is possible for &lt;em&gt;external&lt;/em&gt; site owners to work around this by establishing a &lt;a href=&quot;https://www.w3.org/TR/referrer-policy/&quot;&gt;referrer policy&lt;/a&gt;, may sites do not.&lt;/p&gt;

&lt;p&gt;HTTPS also prevents “man in the middle” attacks and third party content injection by malicious agents and internet service providers (ISPs) which can &lt;a href=&quot;https://zmhenkel.blogspot.com/2013/03/isp-advertisement-injection-cma.html&quot;&gt;insert advertisements&lt;/a&gt;, &lt;a href=&quot;https://blog.ryankearney.com/2013/01/comcast-caught-intercepting-and-altering-your-web-traffic/&quot;&gt;notices&lt;/a&gt;, &lt;a href=&quot;https://www.eff.org/deeplinks/2014/11/verizon-x-uidh&quot;&gt;tracking code&lt;/a&gt; and &lt;a href=&quot;https://arxiv.org/pdf/1602.07128v1.pdf&quot;&gt;malware&lt;/a&gt; and &lt;a href=&quot;https://citizenlab.ca/2015/04/chinas-great-cannon/&quot;&gt;hijack traffic&lt;/a&gt;. &lt;a href=&quot;http://forums.xfinity.com/t5/Customer-Service/Are-you-aware-Comcast-is-injecting-400-lines-of-JavaScript-into/td-p/3009551&quot;&gt;The threat is real&lt;/a&gt;. Accordingly, &lt;a href=&quot;https://https.cio.gov&quot;&gt;HTTPS is required for government agencies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Most domains should use &lt;code class=&quot;highlighter-rouge&quot;&gt;www&lt;/code&gt;&lt;/strong&gt;, because &lt;a href=&quot;http://www.yes-www.org/why-use-www/&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;www&lt;/code&gt; prepares a site to handle the challenges of a multi-server architecture with multiple subdomains&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At the time of writing, &lt;a href=&quot;https://www.google.com&quot;&gt;Google&lt;/a&gt;, &lt;a href=&quot;https://www.wikipedia.org&quot;&gt;Wikipedia&lt;/a&gt;, &lt;a href=&quot;https://www.facebook.com&quot;&gt;Facebook&lt;/a&gt;, &lt;a href=&quot;https://www.reddit.com&quot;&gt;reddit&lt;/a&gt; and &lt;a href=&quot;https://www.apple.com&quot;&gt;Apple&lt;/a&gt; redirect to &lt;code class=&quot;highlighter-rouge&quot;&gt;www&lt;/code&gt;, while &lt;a href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;, &lt;a href=&quot;https://stackoverflow.com&quot;&gt;Stack Overflow&lt;/a&gt;, &lt;a href=&quot;https://twitter.com&quot;&gt;Twitter&lt;/a&gt;, &lt;a href=&quot;https://medium.com&quot;&gt;Medium&lt;/a&gt; and &lt;a href=&quot;https://wordpress.org&quot;&gt;WordPress&lt;/a&gt; redirect to non-&lt;code class=&quot;highlighter-rouge&quot;&gt;www&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Below I have combined the &lt;code class=&quot;highlighter-rouge&quot;&gt;https&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;www&lt;/code&gt; redirects to reduce the number of redirects. Redirects slow page load time and leak SEO value—&lt;strong&gt;eliminate unnecessary redirects&lt;/strong&gt; whenever possible.&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Redirect insecure, www traffic to secure, www&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{HTTPS} !on [OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{HTTP_HOST} !^www\. [NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{HTTP_HOST} ^(?:www\.)?(.*) [NC]
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; (.*) https://www.%1/$1 [R=301,L]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you really don’t care about the benefits of &lt;code class=&quot;highlighter-rouge&quot;&gt;www&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Redirect insecure, www traffic to secure, no-www&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{HTTPS} !on [OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{HTTP_HOST} ^www\. [NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{HTTP_HOST} ^(?:www\.)?(.*) [NC]
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; (.*) https://%1/$1 [R=301,L]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;url-hierarchy&quot;&gt;URL Hierarchy&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;/flat-vs-hierarchical-url-structure-420f178c&quot;&gt;Hierarchical URLs&lt;/a&gt; make it easier for users to navigate and search a site via “virtual breadcrumbs”, enable site owners to gather more detailed analytics data, enable easier URL migrations via redirection, and may enable more efficient crawling from search engines.&lt;/p&gt;

&lt;h3 id=&quot;namespaced-urls&quot;&gt;Namespaced URLs&lt;/h3&gt;

&lt;p&gt;One way to easily add hierarchy to URLs is through namespacing. Namespacing allows you to create new “top level sections” by organizing content into specific verticals. For example, rather than having blog posts live at the same level as your main content, namespace all blog content to &lt;code class=&quot;highlighter-rouge&quot;&gt;/blog/&lt;/code&gt;. This keeps blog URLs isolated from the rest of your content. It’s also easy for users to remember this convention with continued usage.&lt;/p&gt;

&lt;p&gt;Github does a great job of namespacing their URLs. For example:&lt;/p&gt;

&lt;dl class=&quot;compact ul&quot;&gt;
  &lt;dt&gt;&lt;a href=&quot;https://github.com/joeyhoer&quot;&gt;https://github.com/joeyhoer&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;Every path beyond this URL belongs to me&lt;/dd&gt;
  &lt;dt&gt;&lt;a href=&quot;https://github.com/joeyhoer/starter&quot;&gt;https://github.com/joeyhoer/starter&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;Every path beyond this URL belongs to my project &lt;code class=&quot;highlighter-rouge&quot;&gt;starter&lt;/code&gt;&lt;/dd&gt;
  &lt;dt&gt;&lt;a href=&quot;https://github.com/joeyhoer/starter/issues&quot;&gt;https://github.com/joeyhoer/starter/issues&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;Every path beyond this URL references an issue on my project &lt;code class=&quot;highlighter-rouge&quot;&gt;starter&lt;/code&gt;&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;The simplicity and consistency in Github’s URL design has allowed them to scale to millions of users with millions of repositories.&lt;/p&gt;

&lt;h2 id=&quot;url-malleability&quot;&gt;URL Malleability&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;URLs should be malleable&lt;/strong&gt; (“hackable”)—by enabling users to navigate down the URL tree (to the left) by removing segments from the URL—and &lt;em&gt;easily guessable&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;query-strings-for-facets&quot;&gt;Query Strings for Facets&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pages should always work without query strings attached.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;I like to think of querystrings as the knobs of URLs — something to tweak your current view and fine tune it to your liking.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;http://warpspire.com/posts/url-design&quot;&gt;URL Design&lt;/a&gt;, Kyle Neath&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ve written at length about &lt;a href=&quot;/url-structure-of-faceted-navigation-4c940d47&quot;&gt;why query parameters should be used for facets and sorts&lt;/a&gt;. Query parameters are great for URL malleability, because even if a user attempts to hack a URL and “gets it wrong”, they’ll still be on a page with relevant content.&lt;/p&gt;

&lt;h2 id=&quot;url-persistence&quot;&gt;URL Persistence&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Any URL that has ever been exposed to the Internet should live forever: never let any URL die … Remember, people follow links because they want something on your site: the best possible introduction and more valuable than any advertising for attracting new customers.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://www.nngroup.com/articles/fighting-linkrot/&quot;&gt;Fighting Linkrot&lt;/a&gt;, Jakob Nielsen&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once a URL has been minted, you have made a commitment to serve relevant content at that location for as long as possible.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;It is the duty of a Webmaster to allocate URIs which you will be able to stand by in 2 years, in 20 years, in 200 years. This needs thought, and organization, and commitment.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://www.w3.org/Provider/Style/URI.html&quot;&gt;Cool URIs don’t change&lt;/a&gt;, Tim Berners-Lee&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once you’ve launched a new URL, links to that location are no longer under your control. You never know where links to your URLs may exist.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A URL is an agreement to serve something from a predictable location for as long as possible. Once your first visitor hits a URL you’ve implicitly entered into an agreement that if they bookmark the page or hit refresh, they’ll see the same thing.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Don’t change your URLs after they’ve been publicly launched.&lt;/strong&gt; If you absolutely must change your URLs, add redirects …&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;http://warpspire.com/posts/url-design&quot;&gt;URL Design&lt;/a&gt;, Kyle Neath&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When content is not maintained (i.e. when content is removed/archived) during an upgrade and URLs are not appropriately redirected, links from various locations die. Linkrot refers to the tendency for links to increasingly point to resources that have become permanently unavailable over time. &lt;a href=&quot;https://bmcbioinformatics.biomedcentral.com/articles/10.1186/1471-2105-14-S14-S5#Sec2&quot;&gt;The average life span of a web page is 9.3 years.&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Linkrot equals lost business: make sure all URLs live forever and continue to point to relevant pages.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://www.nngroup.com/articles/url-as-ui/&quot;&gt;URL as UI&lt;/a&gt;, Jakob Nielsen&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;URLs that no longer return relevant content erode the credibility of your site and all sites linking to that data.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When someone follows a link and it breaks, they generally lose confidence in the owner of the server. They also are frustrated - emotionally and practically from accomplishing their goal.&lt;/p&gt;

  &lt;p&gt;Enough people complain all the time about dangling links that I hope the damage is obvious. I hope it also obvious that the reputation damage is to the maintainer of the server whose document vanished.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://www.w3.org/Provider/Style/URI.html&quot;&gt;Cool URIs don’t change&lt;/a&gt;, Tim Berners-Lee&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By the way, &lt;a href=&quot;https://medium.com/kaleida/heres-what-happens-if-you-change-the-url-of-a-story-that-s-going-viral-on-facebook-23c1b03b31e1&quot;&gt;changing URLs can also have an impact on social sharing&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;naming-urls&quot;&gt;Naming URLs&lt;/h3&gt;

&lt;p&gt;URLs change when information changes. &lt;strong&gt;If information is subject to change, it has no place in the URL.&lt;/strong&gt; The first step in selecting a naming convention for URLs is identifying common attributes, unique to each piece of content, which will not change over time.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Tip:&lt;/strong&gt; For ecommerce websites, SKUs are typically the best example of a unique identifier that should not change.&lt;/p&gt;

&lt;p&gt;Using the date which the content is published is a good place to start:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The creation date of the document—the date the URI is created—is one thing which will not change. … That is one thing with which it is good to start a URI. If a document is in any way dated, even though it will be of interest for generations, then the date is a good starter.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://www.w3.org/Provider/Style/URI.html&quot;&gt;Cool URIs don’t change&lt;/a&gt;, Tim Berners-Lee&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using the date as the sole indicator for the URL presents a number of challenges. Namely, the date does not convey any relevant information about the content of the linked page—information important for search engines and users making decisions about the quality of a link. Dates also do not scale well for sites which may have multiple pieces of content being published on the same day, the same hour, the same minute, and sometimes even in the same instant. Additionally, users are quick to judge content relevancy based on the date posted and have a prejudice against outdated content. “Information architects” (typically UX designers) avoid dating URLs because they want content to appear &lt;em&gt;evergreen&lt;/em&gt; (always relevant); instead they choose to use the post title/subject as the URL.&lt;/p&gt;

&lt;p&gt;Using the post title in the URL presents it’s own issues:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;leave out … [the] subject. … It always looks good at the time but changes surprisingly fast. … This is a tempting way of organizing a web site—and indeed a tempting way of organizing anything … but has serious drawbacks in the long term. … When you use a topic name in a URI you are binding yourself to some classification. You may in the future prefer a different one. Then, the URI will be liable to break.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://www.w3.org/Provider/Style/URI.html&quot;&gt;Cool URIs don’t change&lt;/a&gt;, Tim Berners-Lee&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As an example, imagine you’ve written a piece of evergreen content titled “Google’s Various Entreprises” and you discover that you’ve misspelled “Enterprises”, and then Google changes its name to &lt;a href=&quot;https://abc.xyz&quot;&gt;Alphabet&lt;/a&gt;, and perhaps you decide later that “Various Ventures” has a nice sound and would be better suited to SEO. What happens to the URL then? Either the URL must change (and be redirected) or your content’s address is permanently mislabeled.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://medium.com&quot;&gt;Medium&lt;/a&gt; and &lt;a href=&quot;https://stackoverflow.com&quot;&gt;Stack Overflow&lt;/a&gt; have both come up with interesting solutions to this issue. Both companies assign an ID to each post and include the ID in the URL along with the current post title. The important thing to note is that in these romanticized URLs &lt;em&gt;the title in the URL is entirely optional&lt;/em&gt;. For example:&lt;/p&gt;

&lt;dl class=&quot;compact ul&quot;&gt;
  &lt;dt&gt;Stack Overflow Question ID &lt;code class=&quot;highlighter-rouge&quot;&gt;10712555&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;ul&gt;
      &lt;li&gt;Minimal URL: &lt;a href=&quot;https://stackoverflow.com/q/10712555/&quot;&gt;https://stackoverflow.com/q/10712555/&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Augmented URL: &lt;a href=&quot;https://stackoverflow.com/questions/10712555/another-title/&quot;&gt;https://stackoverflow.com/questions/10712555/another-title/&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Full URL: &lt;a href=&quot;https://stackoverflow.com/questions/10712555/gitignore-all-files-of-extension-in-directory/&quot;&gt;https://stackoverflow.com/questions/10712555/gitignore-all-files-of-extension-in-directory/&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/dd&gt;
  &lt;dt&gt;Medium Post ID &lt;code class=&quot;highlighter-rouge&quot;&gt;adbec59c7cf4&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;ul&gt;
      &lt;li&gt;Minimal URL: &lt;a href=&quot;https://medium.com/@torgo/adbec59c7cf4&quot;&gt;https://medium.com/@torgo/adbec59c7cf4&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Augmented URL: &lt;a href=&quot;https://medium.com/@torgo/another-title-adbec59c7cf4&quot;&gt;https://medium.com/@torgo/another-title-adbec59c7cf4&lt;/a&gt;&lt;/li&gt;
      &lt;li&gt;Full URL: &lt;a href=&quot;https://medium.com/@torgo/in-defense-of-the-url-adbec59c7cf4&quot;&gt;https://medium.com/@torgo/in-defense-of-the-url-adbec59c7cf4&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;As long as the ID appears in the correct location in the URL, the URL redirects to the correct content. This solution works because it creates persistent URLs that are flexible enough to support romanticized SEO titles which may change over time while enabling scalability.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.reddit.com&quot;&gt;reddit&lt;/a&gt; also takes as similar approach, while &lt;a href=&quot;https://www.youtube.com&quot;&gt;YouTube&lt;/a&gt; uses an 11 character base64 encoded ID for each uploaded video. This technique allows these companies to easily scale into the foreseeable future.&lt;/p&gt;

&lt;div class=&quot;video-embed&quot;&gt;
    &lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/gocwRvLhDf8?rel=0&amp;amp;modestbranding=1&amp;amp;iv_load_policy=3&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;h3 id=&quot;migrating-urls-via-redirection&quot;&gt;Migrating URLs via Redirection&lt;/h3&gt;

&lt;p&gt;A solid URL migration strategy is a critical element of any site migration. Do not neglect to &lt;a href=&quot;/crafting-301s-2fa5ea87&quot;&gt;redirect URLs&lt;/a&gt; appropriately when changing technology and migrating content.&lt;/p&gt;

&lt;h3 id=&quot;one-time-use-urls&quot;&gt;One-Time Use URLs&lt;/h3&gt;

&lt;p&gt;When a user completes and action, they are often presented with a success page/message. Ideally, every user-targeted view on a site would have a unique URL that can be saved. Typically, refreshing a “success page” will return the user to a previous state in the conversion process, meaning success pages cannot be saved or shared. &lt;strong&gt;One time use URLs should be avoided.&lt;/strong&gt; Allow users to refresh the success page to retrieve their order number, subscription information, etc. Better yet, add useful information to that page, just in case the user needs to change something.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;In the past, the development community loved to create URLs which could never be re-used. I like to call them POST-specific URLs—they’re the URLs you see in your address bar after you submit a form, but when you try to copy &amp;amp; pasting the URL into a new tab you get an error.&lt;/p&gt;

  &lt;p&gt;There’s no excuse for these URLs at all. Post-specific URLs are for redirects and APIs—not end-users.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;http://warpspire.com/posts/url-design&quot;&gt;URL Design&lt;/a&gt;, Kyle Neath&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>Fixing Vertical Videos</title>
   <link href="https://joeyhoer.com/fixing-vertical-videos-864d4aec"/>
   <updated>2017-07-25T11:46:32-04:00</updated>
   <id>https://joeyhoer.com/fixing-vertical-videos</id>
   <content type="html">&lt;p&gt;In April 2017, Redditors made &lt;a href=&quot;https://www.reddit.com/r/bestof/comments/66va3e/a_detailed_explanation_of_whats_wrong_with_blurry/&quot;&gt;a compelling argument&lt;/a&gt; against blurred &lt;a href=&quot;https://en.wikipedia.org/wiki/Pillarbox&quot;&gt;pillarboxing&lt;/a&gt;. The primary complaint of this approach is that it increases the file size, which poses a challenge for mobile devices where data is scarce and expensive, thereby increasing the load time. Additionally, pillarboxing makes the video smaller and difficult to see.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Vertical video presented in &lt;em&gt;any&lt;/em&gt; widescreen medium experience similar issues. When viewed on a laptop or desktop (typically 8:5), vertical video embedded in a web page alongside content may either look &lt;em&gt;too small&lt;/em&gt; or &lt;em&gt;push content far down the page&lt;/em&gt;. In the most dramatic examples (where a vertical video “covers” the screen, nearly 65% of the video will remain unseen.&lt;/p&gt;

&lt;h1 id=&quot;history&quot;&gt;History&lt;/h1&gt;

&lt;p&gt;This technique was popularized by television news where vertical video is routinely edited in with widescreen 16:9 video. Maintaining a consistent aspect ratio throughout the program has a more professional feel and can prevent some televisions from stretching the video to fill the screen (which might happen when videos are framed by black bars).&lt;/p&gt;

&lt;p&gt;Today, &lt;a href=&quot;https://www.youtube.com/yt/about/press/&quot;&gt;more than half of YouTube views come from mobile devices&lt;/a&gt;, while &lt;a href=&quot;https://www.facebook.com/iq/articles/the-new-universal-language&quot;&gt;globally, 65% of Facebook video views occur on mobile.&lt;/a&gt;. According to Cisco, &lt;a href=&quot;http://www.cisco.com/c/en/us/solutions/collateral/service-provider/visual-networking-index-vni/mobile-white-paper-c11-520862.pdf&quot;&gt;mobile video traffic accounted for 60 percent of total mobile data traffic in 2016. Cisco also predicts over three-fourths (78 percent) of the world’s mobile data traffic will be video by 2021.&lt;/a&gt; Because people naturally hold their phones in “portrait orientation”, more photos and videos are inevitably being produced in portrait. Likewise, more content is being produced to target these audiences.&lt;/p&gt;

&lt;h1 id=&quot;complaints&quot;&gt;Complaints&lt;/h1&gt;

&lt;p&gt;The internet loves to complain about the use of vertical video, particularity when it comes to blurred pillarboxing, and rightly so.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;By adding that blurry border … the image size [increased] by 16/9*16/9, or a factor of 3.16 … because image compression of large blurry areas is rather efficient, the file size is probably about doubled. Which uses twice as much data for mobile users (and makes it load half as fast).&lt;/p&gt;

  &lt;p&gt;Also, it makes it impossible to see the video clip full size on cellphones, decreasing the size to 1/3 its original size.&lt;/p&gt;

  &lt;p&gt;In summary, you’ve doubled the file size, doubled the loading time, and shrunk the image size to 1/3. I’m so glad I see this all the time now.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://np.reddit.com/r/holdmycatnip/comments/66srx9/catnip_has_been_proven_to_impair_judgement/dgl14dk/&quot;&gt;/u/sloppyjoes7&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Additionally:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Not to mention the it ruins the auto crop when viewing the video on a phone.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://np.reddit.com/r/holdmycatnip/comments/66srx9/catnip_has_been_proven_to_impair_judgement/dgmeqhm/&quot;&gt;/u/Sid6po1nt7&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The topic has even spawned a number of parody videos, including: &lt;a href=&quot;https://www.youtube.com/watch?v=Bt9zSfinwFA&quot;&gt;Vertical Video Syndrome - A PSA&lt;/a&gt;, &lt;a href=&quot;https://www.youtube.com/watch?v=eYLL7-rUGPY&quot;&gt;Turn Your Phone!&lt;/a&gt;, and &lt;a href=&quot;https://www.youtube.com/watch?v=AqHZJe6306k&quot;&gt;Turn Your Phone 90 Degrees&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This “issue” is so pervasive that apps like &lt;a href=&quot;https://horizon.camera&quot;&gt;Horizon Camera&lt;/a&gt; have been developed to attempt to resolve the “problem”.&lt;/p&gt;

&lt;h1 id=&quot;biology&quot;&gt;Biology&lt;/h1&gt;

&lt;p&gt;Perhaps unsurprisingly, humans experience the world in “widescreen”. According to &lt;a href=&quot;http://web.archive.org/web/20170124045754/https://vision.arc.nasa.gov/personnel/al/papers/64vision/17.htm&quot;&gt;a study conducted by NASA&lt;/a&gt;, human vision crops to around a 16:10 aspect ratio.&lt;/p&gt;

&lt;div class=&quot;placeholder &quot; style=&quot;--aspect-ratio:calc(100%/(400/400))&quot;&gt;
  &lt;div style=&quot;max-width:400px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Chart portraying widescreen nature of human vision&quot; width=&quot;400&quot; height=&quot;400&quot; integrity=&quot;sha256-ypfM5KGsF7Qm3Y2CDyhFcg08TvxlhbXyYxvFOsmR+AA=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/nasa-human-vision-ca97cce4a1ac17b426dd8d820f2845720d3c4efc6585b5f2631bc53ac991f800.svg&quot; /&gt;
&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;This diagram shows the normal field of view of a pair of human eyes. The central white portion represents the region seen by both eyes. The gray portions, right and left, represent the regions seen by the right and left eyes, respectively. The cut-off by the brows, cheeks, and nose is shown by the black area. Head and eyes are motionless in this case.&lt;/p&gt;

  &lt;p&gt;– Source: Ruch and Fulton, eds. [25].&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In addition to our physical facial features which obstruct our vision, humans have a natural blind spot in each eye due to lack of receptors (rods or cones) where the optic nerve and blood vessels leave the eye. This can be visualized with the following demonstration:&lt;/p&gt;

&lt;div class=&quot;placeholder &quot; style=&quot;--aspect-ratio:calc(100%/(900/300))&quot;&gt;
  &lt;div style=&quot;max-width:900px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Demonstration of blind spot illusion&quot; width=&quot;900&quot; height=&quot;300&quot; integrity=&quot;sha256-ZIALYoGU+xf7vNNlPbaJqJPaQIxHtfHU+NIMMVjqB5c=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/blind-spot-fovea-disappearing-x-illusion-64800b628194fb17fbbcd3653db689a893da408c47b5f1d4f8d20c3158ea0797.svg&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;Close your right eye, focus on the black dot, and slowly move your eyes closer to the screen until the cross disappears.&lt;/p&gt;

&lt;p&gt;Our brain compensates for these blind spots by “filling in” missing visual information with the surrounding content to create a complete picture of our environment. This is a demonstration of our brain’s natural tendency towards creating uniformity in our environment. Marte Otten, psychology researcher at the University of Amsterdam and lead author of &lt;a href=&quot;http://journals.sagepub.com/doi/abs/10.1177/0956797616672270&quot;&gt;research&lt;/a&gt; on the &lt;a href=&quot;http://www.uniformillusion.com/p/blurriness.html&quot;&gt;uniformity illusion&lt;/a&gt; states that “under some circumstances, a large part of the periphery may become a visual illusion. This effect seems to hold for many basic visual features, indicating that this ‘filling in’ is a general, and fundamental, perceptual mechanism” … “which gives rise to a rich, but illusory, experience of peripheral vision.” While this research was limited to specific tests of the periphery, it is not such a stretch to believe that framing vertical videos with blurred areas makes for a more immersive experience.&lt;/p&gt;

&lt;p&gt;An example of the uniformity illusion applied to blurriness can be seen in the following demonstration:&lt;/p&gt;

&lt;p class=&quot;codepen&quot; data-embed-version=&quot;2&quot; data-theme-id=&quot;dark&quot; data-rerun-position=&quot;hidden&quot; data-slug-hash=&quot;VMZzgR&quot;&gt;
  See the Pen &lt;a href=&quot;//codepen.io/pen/VMZzgR&quot;&gt;Uniformity Illusion Blur&lt;/a&gt;
  by Joey Hoer (&lt;a href=&quot;//codepen.io/joeyhoer&quot;&gt;joeyhoer&lt;/a&gt;).
&lt;/p&gt;

&lt;p&gt;This shows that at worst, blurred pillarboxing provides a more immersive viewing experience, and at best, blurred pillarboxing provides a more comfortable viewing experience by reducing eye stain and cognitive load.&lt;/p&gt;

&lt;h2 id=&quot;a-solution&quot;&gt;A Solution&lt;/h2&gt;

&lt;p&gt;Vertical videos are poorly suited to wide screens, while widescreen videos are poorly suited to the “tall” screens found on mobile devices. A true responsive video solution would provide an optimal experience in both environments. We need a solution that does not add weight, that does not make videos too small or too large.&lt;/p&gt;

&lt;p&gt;Fortunately, JavaScript and &lt;code class=&quot;highlighter-rouge&quot;&gt;canvas&lt;/code&gt; allow us to crate blurred pillarboxing in realtime, using the original video source; that means no unnecessary data over the wire. Additionally, because we can “paint” pixels on the canvas however we choose, we have the ability to create a truly responsive viewing experience that is wide for devices in landscape orientation (like laptops), and tall for devices in portrait orientation.&lt;/p&gt;

&lt;p&gt;The following demonstration uses a small amount of JavaScript to create an optimal viewing experience for vertical videos on all devices:&lt;/p&gt;

&lt;p class=&quot;codepen&quot; data-embed-version=&quot;2&quot; data-theme-id=&quot;dark&quot; data-rerun-position=&quot;hidden&quot; data-slug-hash=&quot;weERyQ&quot;&gt;
  See the Pen &lt;a href=&quot;//codepen.io/pen/weERyQ&quot;&gt;Vertical Video Bars&lt;/a&gt;
  by Joey Hoer (&lt;a href=&quot;//codepen.io/joeyhoer&quot;&gt;joeyhoer&lt;/a&gt;).
&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Always constrain embedded media to the height of the viewport in CSS using &lt;code class=&quot;highlighter-rouge&quot;&gt;vh&lt;/code&gt; units (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;max-height: 100vh&lt;/code&gt;), leaving “padding” between the media and the viewport using &lt;code class=&quot;highlighter-rouge&quot;&gt;calc()&lt;/code&gt;.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>The Curious Case of Route 53 and New Relic Compatibility</title>
   <link href="https://joeyhoer.com/route-53-new-relic-731ce35d"/>
   <updated>2017-04-02T18:52:58-04:00</updated>
   <id>https://joeyhoer.com/route-53-new-relic</id>
   <content type="html">&lt;p&gt;Last week I received an email from a client outlining an issue they were encountering with Amazon’s Route 53 health checks which lead to a discovery about how Amazon’s string based health checks operate and how that can create issues with the performance monitoring tool New Relic.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;the-incident&quot;&gt;The Incident&lt;/h2&gt;

&lt;p&gt;I was sitting at my desk when an unsuspecting email appeared in my inbox:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Joey,&lt;/p&gt;

  &lt;p&gt;Something changed and Amazon can no longer reliably monitor the site’s health. See the first graph below. This started around 1AM last Friday. Since then you can see that the health percent from each test point is less than 60% and thus we can no longer rely on this to monitor the site. The monitoring used to be perfectly spot on, so something has changed.&lt;/p&gt;

  &lt;p&gt;I’ve attached a screenshot of the health check configuration. Why this is failing makes no sense, but I’ve tripled checked. Amazon’s test points are whitelisted on the firewall and have no problems resolving the correct load balancer IP address, but for whatever reason it cannot find the [search string] on the main page.&lt;/p&gt;

  &lt;p&gt;Also attached is a second screenshot of the status of all the most recent checks and their result for reference.&lt;/p&gt;

  &lt;p&gt;Thanks,&lt;br /&gt;
Jeff&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;placeholder &quot; style=&quot;--aspect-ratio:calc(100%/(761/266))&quot;&gt;
  &lt;div style=&quot;max-width:761px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Screenshot of Amazon Route 53 search string configuration&quot; width=&quot;761&quot; height=&quot;266&quot; integrity=&quot;sha256-damEmotmRAcI01blpXGq/elQMw0V+akZmNY6xI4YrXw=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/route-53-issue-configuration-75a9849a8b66440708d356e5a571aafde950330d15f9a91998d63ac48e18ad7c.png&quot; /&gt;
&lt;/div&gt;

&lt;div class=&quot;placeholder &quot; style=&quot;--aspect-ratio:calc(100%/(800/254))&quot;&gt;
  &lt;div style=&quot;max-width:800px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Screenshot of Amazon Route 53 health check graph&quot; width=&quot;800&quot; height=&quot;254&quot; integrity=&quot;sha256-UfRKx//te3onm9YPI5XH9JNKjrK7Sf9KJBLPK/KltDM=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/route-53-issue-graph-51f44ac7ffed7b7a279bd60f2395c7f4934a8eb2bb49ff4a2412cf2bf2a5b433.png&quot; /&gt;
&lt;/div&gt;

&lt;div class=&quot;placeholder &quot; style=&quot;--aspect-ratio:calc(100%/(800/231))&quot;&gt;
  &lt;div style=&quot;max-width:800px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Screenshot of Amazon Route 53 health check status table&quot; width=&quot;800&quot; height=&quot;231&quot; integrity=&quot;sha256-jTU7bq1URC9XO5Jy1XpMbqm3SDz82Vks6KgGwuzJipk=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/route-53-issue-logs-8d353b6ead54442f573b9272d57a4c6ea9b7483cfcd9592ce8a806c2ecc98a99.png&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;the-clues&quot;&gt;The Clues&lt;/h2&gt;

&lt;p&gt;While I’m not particularly well versed in Amazon’s health check system, I could tell based on the screenshots that the health check was configured to use a &lt;strong&gt;search string&lt;/strong&gt; which is an approach commonly used in other health check systems to ensure that a 200 response isn’t, in fact, a blank page or error page. I knew that I could reproduce this health check myself with the following command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-sL&lt;/span&gt; https://www.site.com | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$search&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, running this command multiple times did not reproduce the issue. The search string was found every time.&lt;/p&gt;

&lt;p&gt;So I made a cursory search of Google to learn more about &lt;a href=&quot;http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/dns-failover-determining-health-of-endpoints.html&quot;&gt;Amazon’s Route 53&lt;/a&gt;, and found:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[When using] health checks with string matching… &lt;strong&gt;The string must appear entirely in the first 5120 bytes of the response body&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ah-ha! So Route 53 has a byte limit restriction on the string matching health check. Armed with that information, I was able to amend the previous manual check, and this time I &lt;em&gt;was&lt;/em&gt; able to reproduce the issue.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;curl &lt;span class=&quot;nt&quot;&gt;-sL&lt;/span&gt; https://www.site.com | head &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; 5120 | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$search&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;-string&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now, I know from experience (and based on Jeff’s email) that this site has &lt;strong&gt;two web servers&lt;/strong&gt; behind a load balancer. Fortunately for me, our server configuration exposes which server responds to a particular request, so I was able to see that while responses from “Web 1” were succeeding, responses from “Web 2” were failing.&lt;/p&gt;

&lt;p&gt;Looking at the responses from both servers, it became clear that there was JavaScript at the top of each response that was different between the two servers. In the response from “Web 2” there was more JavaScript. So much JavaScript the the page title (which included the first occurrence of search string on the page) was pushed just beyond Amazon’s 5120 byte limit.&lt;/p&gt;

&lt;p&gt;This was concerning; given our automated deployment process, having different code across the two servers should not be possible.&lt;/p&gt;

&lt;p&gt;After a moment, I realized that the JavaScript was a requirement of a performance monitoring tool we use: New Relic. …but the question remains, if it wasn’t included in the codebase, &lt;em&gt;why was it different&lt;/em&gt;? How did it get on the page?&lt;/p&gt;

&lt;h2 id=&quot;the-culprit&quot;&gt;The Culprit&lt;/h2&gt;

&lt;p&gt;So I fired up Google again, this time searching for more information on New Relic’s JavaScript, and found &lt;a href=&quot;https://docs.newrelic.com/docs/agents/php-agent/features/page-load-timing-php&quot;&gt;New Relic documentation reports&lt;/a&gt; “the PHP agent will automatically inject the Browser JavaScript into your pages.”&lt;/p&gt;

&lt;p&gt;Great, so now we just need to check which version of New Relic’s PHP agent is installed on each server. As I suspected, the versions across the two server’s are indeed different:&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Server&lt;/th&gt;
      &lt;th&gt;Agent&lt;/th&gt;
      &lt;th&gt;Version&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Web 1&lt;/td&gt;
      &lt;td&gt;newrelic-php5.x86_64&lt;/td&gt;
      &lt;td&gt;6.5.0.166-1&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Web 2&lt;/td&gt;
      &lt;td&gt;newrelic-php5.x86_64&lt;/td&gt;
      &lt;td&gt;7.1.0.187-1&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;As it turned out, our hosting provider had upgraded the New Relic Agent while investigating an issue the week prior.&lt;/p&gt;

&lt;p&gt;As a temporary solution I suggested checking for another unique string that appear prior to the New Relic JavaScript and was unlikely to appear on a blank page or error page.&lt;/p&gt;

&lt;p&gt;Ultimately, I learned that given Route 53’s 5120 byte limit to string matches, it may be difficult to use newer versions of New Relic’s agent along with Route 53 unless modifications are to the header of the document, like adding a unique string very early on in the document.&lt;/p&gt;

&lt;p&gt;Case closed.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Robots Directives</title>
   <link href="https://joeyhoer.com/robots-directives-65ad367a"/>
   <updated>2016-11-28T21:45:24-05:00</updated>
   <id>https://joeyhoer.com/robots-directives</id>
   <content type="html">&lt;p&gt;Search engines are powerful tools, yet it is surprisingly simple to provide direction to search engines and ensure that content is shown (and hidden) appropriately in search results. Likewise, it is surprisingly easy to make mistakes and instruct a search engine to show (or hide) content unintentionally. Understanding the basic tools that search engines provide content authors to guide search robots enables you to have important content appear in search results, and prevents you from making costly mistakes.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;indexing-methods&quot;&gt;Indexing Methods&lt;/h2&gt;

&lt;p&gt;Search engines use three basic actions to discover new content to present in search results:&lt;/p&gt;

&lt;dl class=&quot;compact ol&quot;&gt;
  &lt;dt&gt;Crawl&lt;/dt&gt;
  &lt;dd&gt;page content is read by search engines&lt;/dd&gt;
  &lt;dt&gt;Follow&lt;/dt&gt;
  &lt;dd&gt;in-page links are traversed by search engines&lt;/dd&gt;
  &lt;dt&gt;Index&lt;/dt&gt;
  &lt;dd&gt;page is recorded and may appear in search results&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Search engines provide content authors with tools to control behavior when new content is discovered: the &lt;code class=&quot;highlighter-rouge&quot;&gt;robots.txt&lt;/code&gt; file and the robots tag.&lt;/p&gt;

&lt;p&gt;The robots tag controls &lt;em&gt;indexing&lt;/em&gt; and &lt;em&gt;following&lt;/em&gt;, while the &lt;code class=&quot;highlighter-rouge&quot;&gt;robots.txt&lt;/code&gt; file controls &lt;em&gt;crawling&lt;/em&gt; (evidence of which can be seen in the Google search results: “A description for this result is not available because of this site’s robots.txt”).&lt;/p&gt;

&lt;div class=&quot;placeholder screenshot-window&quot; style=&quot;--aspect-ratio:calc(100%/(850/400))&quot;&gt;
  &lt;div style=&quot;max-width:850px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Screenshot of Google Search results exhibiting content blocked by robots.txt&quot; width=&quot;850&quot; height=&quot;400&quot; integrity=&quot;sha256-uXDxM6MpsMFZS9uwNGoEMXED31C9Qq//t22e/x2r7yE=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/serp-robots-txt-b970f133a329b0c1594bdbb0346a04317103df50bd42afffb76d9eff1dabef21.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;There are a number of &lt;em&gt;directives&lt;/em&gt; (described by &lt;a href=&quot;https://developers.google.com/webmasters/control-crawl-index/docs/robots_txt#basic-definitions&quot;&gt;Google’s robots.txt specification&lt;/a&gt; as “guidelines for a crawler or group of crawlers”) that allow content authors to prevent search engines from crawling, following and indexing content; &lt;a href=&quot;https://developers.google.com/webmasters/control-crawl-index/docs/robots_txt#group-member-records&quot;&gt;by default, there are no restrictions for crawling&lt;/a&gt;; &lt;a href=&quot;https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag#valid-indexing--serving-directives&quot;&gt;pages are treated as crawlable [and] indexable… unless permission is specifically denied&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The primary directives for restricting search engine behavior are:&lt;/p&gt;

&lt;dl class=&quot;compact ul&quot;&gt;
  &lt;dt&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;disallow&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;Do not crawl, access (i.e. read content from) the specified paths.&lt;/dd&gt;
  &lt;dt&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;noindex&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;Do not index this page (i.e do not show in search results).&lt;/dd&gt;
  &lt;dt&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;nofollow&lt;/code&gt;&lt;/dt&gt;
  &lt;dd&gt;Do not follow (i.e. attempt to crawl) links on the page.&lt;/dd&gt;
&lt;/dl&gt;

&lt;h2 id=&quot;crawl-directives-in-robotstxt&quot;&gt;Crawl Directives in &lt;code class=&quot;highlighter-rouge&quot;&gt;robots.txt&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;robots.txt&lt;/code&gt; is a plain text file accessible at the top-level directory of the host (domain) encoded in UTF-8.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;a href=&quot;https://developers.google.com/webmasters/control-crawl-index/docs/robots_txt#examples-of-valid-robotstxt-urls&quot;&gt;Crawlers will not check for robots.txt files in subdirectories.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In &lt;a href=&quot;https://www.gnu.org/software/bash/manual/html_node/Comments.html#Comments&quot;&gt;standard UNIX fashion&lt;/a&gt;, comments can be included at any location in the file using the “#” character; all remaining characters on that line are treated as a comment and are ignored.&lt;/p&gt;

&lt;p&gt;Interestingly, “&lt;a href=&quot;https://developers.google.com/webmasters/control-crawl-index/docs/robots_txt#file-format&quot;&gt;a maximum file size may be enforced&lt;/a&gt; per crawler. Content which exceeds the maximum file size may be ignored. Google currently enforces a size limit of 500 kilobytes”.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;robots.txt&lt;/code&gt; file is broken up into sections (called “groups”), each defined/separated by a &lt;code class=&quot;highlighter-rouge&quot;&gt;user-agent&lt;/code&gt; record.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Only one group of group-member records is valid for a particular crawler… All other groups of records are ignored by the crawler… The order of the groups within the &lt;code class=&quot;highlighter-rouge&quot;&gt;robots.txt&lt;/code&gt; file is irrelevant.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://developers.google.com/webmasters/control-crawl-index/docs/robots_txt#order-of-precedence-for-user-agents&quot;&gt;Google’s robots.txt specification: Order of precedence for user-agents&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;non-human-readable-files&quot;&gt;Non-human Readable Files&lt;/h3&gt;

&lt;p&gt;Ensure that search engines &lt;em&gt;can&lt;/em&gt; crawl CSS and JavaScript files.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Disallowing crawling of JavaScript or CSS files in your site’s &lt;code class=&quot;highlighter-rouge&quot;&gt;robots.txt&lt;/code&gt; directly harms how well our algorithms render and index your content and can result in suboptimal rankings.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2014/10/updating-our-technical-webmaster.html&quot;&gt;Updating our technical Webmaster Guidelines&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;If resources like JavaScript or CSS in separate files are blocked (say, with &lt;code class=&quot;highlighter-rouge&quot;&gt;robots.txt&lt;/code&gt;) so that Googlebot can’t retrieve them, our indexing systems won’t be able to see your site like an average user. We recommend allowing Googlebot to retrieve JavaScript and CSS so that your content can be indexed better. This is especially important for mobile websites, where external resources like CSS and JavaScript help our algorithms understand that the pages are optimized for mobile.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2014/05/understanding-web-pages-better.html&quot;&gt;Understanding web pages better&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;robots-tag&quot;&gt;Robots Tag&lt;/h2&gt;

&lt;p&gt;The robots tag may be defined either directly in the HTML as a &lt;code class=&quot;highlighter-rouge&quot;&gt;meta&lt;/code&gt; tag, or in the HTTP &lt;code class=&quot;highlighter-rouge&quot;&gt;X-Robots-Tag&lt;/code&gt; header.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-html&quot; data-lang=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;meta&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;robots&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;content=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;noindex, nofollow&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;X-Robots-Tag: noindex, nofollow
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The quantity and order of robots directives does not matter, if multiple directives exist, the most restrictive directive will be used:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If competing directives are encountered by our crawlers we will use the most restrictive directive we find.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag#using-the-x-robots-tag-http-header&quot;&gt;Robots meta tag and X-Robots-Tag HTTP header specifications&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;noindex-a-section-of-a-site&quot;&gt;Noindex a Section of a Site&lt;/h3&gt;

&lt;p&gt;Using the &lt;code class=&quot;highlighter-rouge&quot;&gt;X-Robots-Tag&lt;/code&gt; HTTP header, it is possible to prevent search engines from indexing sections of a site, and/or particular file types:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Prevent indexing of all pages with `/category/` in the URL&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{THE_REQUEST} /category/
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; ^ - [ENV=NOINDEX:true]
&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;IfModule&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt; mod_headers.c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;    &lt;span class=&quot;nc&quot;&gt;Header&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;set&lt;/span&gt; X-Robots-Tag &quot;noindex&quot; env=NOINDEX
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;IfModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Prevent indexing of image files (.png, .jpeg, .jpg, .gif)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt; ~ &quot;\.(png|jpe?g|gif)$&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;  &lt;span class=&quot;nc&quot;&gt;Header&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;set&lt;/span&gt; X-Robots-Tag &quot;noindex&quot;
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;de-indexing-catch-22&quot;&gt;De-indexing Catch 22&lt;/h2&gt;

&lt;p&gt;Even when the &lt;code class=&quot;highlighter-rouge&quot;&gt;robots.txt&lt;/code&gt; is set to disallow, and the header of every page includes a &lt;code class=&quot;highlighter-rouge&quot;&gt;noindex, nofollow&lt;/code&gt; directive, pages which have already been indexed may continue to appear in the index – “stuck” in perpetuity. This happens because the &lt;code class=&quot;highlighter-rouge&quot;&gt;robots.txt&lt;/code&gt; file, when set to disallow, instructs the crawler not to crawl (i.e. read the contents of) the page, so the &lt;code class=&quot;highlighter-rouge&quot;&gt;noindex&lt;/code&gt; directive is never read and the search engine does not know to remove the pages from the index. As Google Search Console’s Remove URLs tool instructs, &lt;strong&gt;the best way to de-index the site is to allow the search engine to crawl the site.&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;placeholder screenshot-window&quot; style=&quot;--aspect-ratio:calc(100%/(850/520))&quot;&gt;
  &lt;div style=&quot;max-width:850px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Screenshot of Google Search Console's Remove URLs tool&quot; width=&quot;850&quot; height=&quot;520&quot; integrity=&quot;sha256-6ns77qNZSP2oDYCjeMkhfHbFzjWWBbwpx615jOZu1Z4=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/gsc-remove-urls-ea7b3beea35948fda80d80a378c9217c76c5ce359605bc29c7ad798ce66ed59e.png&quot; /&gt;
&lt;/div&gt;

&lt;blockquote&gt;
  &lt;p&gt;To block a page from appearing in Google Search results permanently… add a NOINDEX tag to the page and allow it to be crawled by Googlebot.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Allow search engines to crawl pages by remove &lt;code class=&quot;highlighter-rouge&quot;&gt;disallow&lt;/code&gt; from &lt;code class=&quot;highlighter-rouge&quot;&gt;robots.txt&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Add a &lt;code class=&quot;highlighter-rouge&quot;&gt;noindex&lt;/code&gt; HTTP header or meta tag to the pages&lt;/li&gt;
  &lt;li&gt;Claim the domain (in Google Search Console) and submit the URLs for removal&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; To remove an entire domain from Google Search results, enter a single forward slash (“/”) into the prompt in Google Search Console’s Remove URLs tool.&lt;/p&gt;

&lt;div class=&quot;placeholder screenshot-window&quot; style=&quot;--aspect-ratio:calc(100%/(412/83))&quot;&gt;
  &lt;div style=&quot;max-width:412px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Screenshot of Google Search Console's Remove URLs prompt&quot; width=&quot;412&quot; height=&quot;83&quot; integrity=&quot;sha256-peiA3S5fHKwATVjFEDFowI1cuJIeS2vbgRss3/UENMw=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/gsc-removal-prompt-a5e880dd2e5f1cac004d58c5103168c08d5cb8921e4b6bdb811b2cdff50434cc.png&quot; /&gt;
&lt;/div&gt;

&lt;/div&gt;

&lt;p&gt;Once you’ve successfully removed the URLs from Google Search results, the SERP page should appear empty.&lt;/p&gt;

&lt;div class=&quot;placeholder screenshot-window&quot; style=&quot;--aspect-ratio:calc(100%/(850/400))&quot;&gt;
  &lt;div style=&quot;max-width:850px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Screenshot of empty Google Search results&quot; width=&quot;850&quot; height=&quot;400&quot; integrity=&quot;sha256-bUKqh6657k5J9OhXfExSEHOIj1dS0Nvs8utcvZOODb8=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/serp-no-results-6d42aa87aeb9ee4e49f4e8577c4c521073888f5752d0dbecf2eb5cbd938e0dbf.png&quot; /&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Canonical Links</title>
   <link href="https://joeyhoer.com/canonical-links-a0f1b14f"/>
   <updated>2016-11-25T00:36:00-05:00</updated>
   <id>https://joeyhoer.com/canonical-links</id>
   <content type="html">&lt;p&gt;Canonical links are frequently misunderstood by developers, marketers, content authors and SEOs. When used correctly, &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; is a powerful tool that can improve search results for visitors. However, when used incorrectly, canonical links can be detrimental to search results, and may in some cases be ignored by search engines entirely.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;what-is-canonicalization&quot;&gt;What is Canonicalization?&lt;/h2&gt;

&lt;p&gt;Canonical links allow authors to specify a preferred version of a web page. In other words, these links instruct search engines of which URL should appear in search results for a particular piece of content.&lt;/p&gt;

&lt;p&gt;When authoring content on the web, it is common to present the same material at different URLs. This frequently occurs when a site is accessible via more than one protocol or subdomain and when index files are directly accessible. For example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;http://example.com&lt;/li&gt;
  &lt;li&gt;http://example.com/index.html&lt;/li&gt;
  &lt;li&gt;http://www.example.com&lt;/li&gt;
  &lt;li&gt;http://www.example.com/index.html&lt;/li&gt;
  &lt;li&gt;https://example.com&lt;/li&gt;
  &lt;li&gt;https://example.com/index.html&lt;/li&gt;
  &lt;li&gt;https://www.example.com&lt;/li&gt;
  &lt;li&gt;https://www.example.com/index.html&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, this may also occur when content is organized into different categories or collections, and when all or part of a body of content is syndicated on other sites.&lt;/p&gt;

&lt;p&gt;This &lt;a href=&quot;https://support.google.com/webmasters/answer/66359&quot;&gt;&lt;em&gt;duplicate content&lt;/em&gt;&lt;/a&gt; presents a problem for search engines, which aim to improve user experience. If a search engine was to index each of the URLs above, a visitor could be presented with repetitive content in search results, leading to a poor user experience. Therefore, search engines “[try] hard to index and show pages with distinct information.”&lt;/p&gt;

&lt;p&gt;When a search engine encounters “substantive blocks” of duplicate content, the search engine must select a single version of the content to show in search results.&lt;/p&gt;

&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The concept of a “duplicate content penalty” is largely a myth. As Google notes:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;There’s no such thing as a “duplicate content penalty.” At least, not in the way most people mean when they say that.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://webmasters.googleblog.com/2008/09/demystifying-duplicate-content-penalty.html&quot;&gt;Demystifying the “duplicate content penalty”&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;Duplicate content on a site is not grounds for action on that site unless it appears that the intent of the duplicate content is to be deceptive and manipulate search engine results.&lt;/p&gt;

  &lt;p&gt;…Deceptive practices like this can result in a poor user experience, when a visitor sees substantially the same content repeated within a set of search results.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://support.google.com/webmasters/answer/66359&quot;&gt;Google Search Console Help: Duplicate content&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;/div&gt;

&lt;p&gt;Through the use of canonical links, authors can effectively inform search engines to index content only at one URL.&lt;/p&gt;

&lt;h2 id=&quot;defining-a-canonical-link&quot;&gt;Defining a Canonical Link&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; link may be added via either an HTML tag or &lt;a href=&quot;https://support.google.com/webmasters/answer/139066#6&quot;&gt;HTTP header&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-html&quot; data-lang=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;canonical&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;http://www.example.com/canonical.html&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Link: &amp;lt;http://www.example.com/canonical.html&amp;gt;; rel=&quot;canonical&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; Any HTML link may be specified as an HTTP header according to &lt;a href=&quot;https://tools.ietf.org/html/rfc5988#section-5&quot;&gt;RFC5988§5&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;canonical-indexing&quot;&gt;Canonical Indexing&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; link is an SEO &lt;em&gt;hint&lt;/em&gt; &lt;strong&gt;not&lt;/strong&gt; a directive. While canonicalized URLs are not &lt;em&gt;supposed&lt;/em&gt; to be indexed or show up in search results, there are some circumstances where search engines may choose not to honor canonical links.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Are Non-Canonical Pages Indexed?&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;For all practical purposes – no. &lt;em&gt;If Google honors a &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; tag&lt;/em&gt;, then the non-canonical page is not eligible for ranking. It will not have a unique cached copy, and it will not appear in the public index via a “site:” search.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://moz.com/blog/rel-confused-answers-to-your-rel-canonical-questions&quot;&gt;Rel=Confused? Answers to Your Rel=Canonical Questions
&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nevertheless, Google may ignore a canonical URL if:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Content is significantly different on the canonicalized URL.&lt;/li&gt;
  &lt;li&gt;Multiple canonical links exist on a URL.&lt;/li&gt;
  &lt;li&gt;A canonical loop exists wherein a canonicalized paged links back to the original page.&lt;/li&gt;
  &lt;li&gt;The canonicalized page is redirected.
    &lt;ul&gt;
      &lt;li&gt;According to Yahoo, &lt;a href=&quot;http://www.ysearchblog.com/2009/02/12/fighting-duplication-adding-more-arrows-to-your-quiver/&quot;&gt;canonical links are transitive&lt;/a&gt;, but this can result in unexpected behavior.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;The target does not exist.
    &lt;ul&gt;
      &lt;li&gt;The URL is empty, broken, or results in an error or “soft 404”.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Google believes the link is malicious.&lt;/li&gt;
  &lt;li&gt;The link does not appear in the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, or if the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt; contains unusual content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; The above list outlines &lt;em&gt;some&lt;/em&gt; of the known instances in which Google may elect to disregard a canonical link. &lt;strong&gt;Other examples may exist.&lt;/strong&gt;&lt;/p&gt;

&lt;h3 id=&quot;content-discrepancies&quot;&gt;Content Discrepancies&lt;/h3&gt;

&lt;p&gt;Canonical links are designed to connect content that is substantively similar. If the canonicalized content does not closely match the original content, search engines are unlikely to respect the link.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A large portion of the duplicate page’s content should be present on the canonical version… for example, if [two pages are] only topically similar but not extremely close in exact words, the canonical designation might be disregarded by search engines.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2013/04/5-common-mistakes-with-relcanonical.html&quot;&gt;5 common mistakes with rel=canonical&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; While not recommended, it is possible to use &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; on dissimilar content; doing so may cause search engines to stop trusting your site’s canonical links.&lt;/p&gt;

&lt;h3 id=&quot;link-quantity&quot;&gt;Link Quantity&lt;/h3&gt;

&lt;p&gt;The number of canonical links that exist for a specific URL is significant. When multiple canonical declarations exist for a URL, Google will “&lt;a href=&quot;https://webmasters.googleblog.com/2013/04/5-common-mistakes-with-relcanonical.html&quot;&gt;likely ignore all the &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; hints&lt;/a&gt;”.&lt;/p&gt;

&lt;p&gt;If multiple canonical links do exist for a URL, be careful to ensure that the links do not contradict one another by verifying that all canonical links point to the same destination.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Use the HTML &lt;em&gt;or&lt;/em&gt; the HTTP method of declaring canonical links consistently across a site to avoid overlap and accidental conflicts.&lt;/p&gt;

&lt;h3 id=&quot;link-placement&quot;&gt;Link Placement&lt;/h3&gt;

&lt;p&gt;The position of canonical links in the HTML document is also significant. Ensure that the &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; tag appears as close to the beginning of the document as possible, and strive to maintain valid HTML – particularly in the document &lt;code class=&quot;highlighter-rouge&quot;&gt;head&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; link tag should only appear in the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of an HTML document. Additionally, to avoid HTML parsing issues, it’s good to include the &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; as early as possible in the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;head&amp;gt;&lt;/code&gt;. When we encounter a &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; designation in the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;body&amp;gt;&lt;/code&gt;, it’s disregarded.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2013/04/5-common-mistakes-with-relcanonical.html&quot;&gt;5 common mistakes with rel=canonical&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Matt Cutts explains why this is the case:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If Google trusted &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; in the HTML &lt;code class=&quot;highlighter-rouge&quot;&gt;body&lt;/code&gt;, we’d see far more attacks where people would drop a&lt;code class=&quot;highlighter-rouge&quot;&gt; rel=canonical&lt;/code&gt; on part of a web page to try to hijack it.&lt;/p&gt;

  &lt;p&gt;…so now we come to another corner case… we probably won’t trust a &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; if we see weird stuff in your &lt;code class=&quot;highlighter-rouge&quot;&gt;HEAD&lt;/code&gt; section… [because] we may assume that someone forgot to close the HEAD section[, and] we don’t allow &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; in the &lt;code class=&quot;highlighter-rouge&quot;&gt;BODY&lt;/code&gt;.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://www.mattcutts.com/blog/rel-canonical-html-head/&quot;&gt;A rel=canonical corner case&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;broken-and-malicious-links&quot;&gt;Broken and Malicious Links&lt;/h3&gt;

&lt;p&gt;An even poorer user experience than indexing duplicate content would be indexing the wrong content, no content, or malicious content. Matt Cutts outlines scenarios in which Google would ignore such links:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;We take &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; URLs as a strong hint, but in some cases we won’t use them:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;For example, if we think you’re shooting yourself in the foot by accident (pointing a &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; toward a non-existent/404 page), we’d reserve the right not to use the destination URL you specify with &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt;.&lt;/li&gt;
    &lt;li&gt;Another example where we might not go with your &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; preference: if we think your website has been hacked and the hacker added a malicious &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt;.&lt;/li&gt;
  &lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;canonicalization-vs-redirection&quot;&gt;Canonicalization vs Redirection&lt;/h2&gt;

&lt;p&gt;Because search engines can choose to ignore canonical links, &lt;strong&gt;redirection is preferred&lt;/strong&gt;; as &lt;a href=&quot;https://www.youtube.com/watch?v=zW5UL3lzBOA&amp;amp;t=65&quot;&gt;Matt Cutts explains&lt;/a&gt;, “regarding 301 redirects vs &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt;, in general I would use 301 redirects… they’re more widely supported.” He goes on to say, “the &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; is more appropriate for when you can’t get to the server headers… if you can do 301 redirects… do [that]. If you don’t have the ability or option to do 301 redirects… &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; makes sense.”&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://support.google.com/webmasters/answer/139066#4&quot;&gt;Google echoes this in the Search Console Help guidelines “Use canonical URLs”&lt;/a&gt; the use of “301 redirects to send traffic from [undesirable] URLs to your preferred URL. A server-side 301 redirect is the best way to ensure that users and search engines are directed to the correct page.”&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; Because canonicalized pages are actually separate entities, visitor behavior is recorded separately for each unique URL in analytics tools. This may or may not be desirable.&lt;/p&gt;

&lt;p&gt;Barring any technical reasons preventing redirection, another acceptable reason to use a canonical link over a redirect is if redirection would negatively impact user experience (e.g. categorization with breadcrumbs, pagination).&lt;/p&gt;

&lt;h2 id=&quot;canonicalized-pagination&quot;&gt;Canonicalized Pagination&lt;/h2&gt;

&lt;p&gt;A common mistake when canonicalizing pages is to canonicalize paginated content to the first page of content. Because the content of the first page is not representative of or substantively similar to the remaining paginated content, linking these pages to the first page essentially instructs search engines not to index a majority of the paginated content. This may lead search engines to ignore the canonical links.&lt;/p&gt;

&lt;p&gt;Because “searchers commonly prefer to view a whole article or category on a single page,” Google &lt;a href=&quot;https://support.google.com/webmasters/answer/1663744&quot;&gt;recommends&lt;/a&gt; linking to a “View All” version of the content, if one exists and does not present any user experience issues. In fact, “if [Google thinks] this is what the searcher is looking for, [they] try to show the View All page in search results.”&lt;/p&gt;

&lt;h2 id=&quot;canonicalizing-facets&quot;&gt;Canonicalizing Facets&lt;/h2&gt;

&lt;p&gt;While &lt;a href=&quot;/url-structure-of-faceted-navigation-4c940d47&quot;&gt;facets&lt;/a&gt; can and likely should be canonicalized, &lt;a href=&quot;https://support.google.com/webmasters/answer/139066#5&quot;&gt;Google recommends using the Google Search Console Parameter Handling tool&lt;/a&gt; to “tell Google about any parameters you would like ignored.” As Google notes, “ignoring certain parameters can reduce duplicate content in Google’s index, and make your site more crawlable.”&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; Search engines impose a &lt;em&gt;crawl budget&lt;/em&gt; on sites (i.e. content is crawled and updated over time, not immediately). Providing search engines with valuable information about how your site is structured will allow search engines to index content more efficiently.&lt;/p&gt;

&lt;h2 id=&quot;combining-with-robots-directives&quot;&gt;Combining with Robots Directives&lt;/h2&gt;

&lt;p&gt;Implementing &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; in combination with robots directives like &lt;code class=&quot;highlighter-rouge&quot;&gt;noindex&lt;/code&gt; is not advised, because &lt;code class=&quot;highlighter-rouge&quot;&gt;noindex&lt;/code&gt; prevents pages from pass PageRank to the canonical version. As Google’s &lt;a href=&quot;https://plus.google.com/+JohnELincoln/posts/TCJHwdZHdQc&quot;&gt;John Mueller reports&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;You should not combine the &lt;code class=&quot;highlighter-rouge&quot;&gt;noindex&lt;/code&gt; with a re-canonical pointing at an indexable URL (the rel=canonical says they’re equivalent, the &lt;code class=&quot;highlighter-rouge&quot;&gt;noindex&lt;/code&gt; says they’re pretty much opposites)… pick one, but not both.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://plus.google.com/+JohnELincoln/posts/TCJHwdZHdQc&quot;&gt;John Mueller’s comment on canonical&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And &lt;a href=&quot;https://productforums.google.com/forum/#!msg/webmasters/0sqRrolO_Ss/igOdQIjwKdEJ&quot;&gt;again&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[When using &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt;,] only use the &lt;code class=&quot;highlighter-rouge&quot;&gt;rel=canonical&lt;/code&gt; link element… One reason for this is that we sometimes find a non-canonical URL first. If this URL has a &lt;code class=&quot;highlighter-rouge&quot;&gt;noindex&lt;/code&gt; robots meta tag, we might decide not to index anything until we crawl and index the canonical URL.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://productforums.google.com/forum/#!msg/webmasters/0sqRrolO_Ss/igOdQIjwKdEJ&quot;&gt;John Mueller’s answer to Canonical conflicts with NOINDEX?&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>The URL Structure of Faceted Navigation</title>
   <link href="https://joeyhoer.com/url-structure-of-faceted-navigation-4c940d47"/>
   <updated>2016-10-16T16:08:35-04:00</updated>
   <id>https://joeyhoer.com/url-structure-of-faceted-navigation</id>
   <content type="html">&lt;p&gt;Faceted navigation is the collection of UI elements and functionality which provide the ability to filter and refine category views. There is some debate in the SEO, UX and general web development communities about the best way to present faceted navigation in the URL.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Faceted navigation, such as filtering by color or price range, can be helpful for your visitors, but it’s often not search-friendly since it creates many combinations of URLs with duplicative content.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://webmasters.googleblog.com/2014/02/faceted-navigation-best-and-5-of-worst.html&quot;&gt;Faceted navigation best (and 5 of the worst) practices&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The correct way to denote facets in a URL is through the use of query parameters. However, some believe that virtual subdirectories present a better alternative for SEO and UX. We’ll compare the different options for including facets in a URL, starting with simplistic examples of each method.&lt;/p&gt;

&lt;!--more--&gt;

&lt;dl class=&quot;compact ul u-break-word&quot;&gt;
  &lt;dt&gt;Query parameters&lt;/dt&gt;
  &lt;dd&gt;https://example.com/category?option=true&lt;/dd&gt;
  &lt;dt&gt;Virtual subdirectories&lt;/dt&gt;
  &lt;dd&gt;https://example.com/category/filter/option/true&lt;/dd&gt;
&lt;/dl&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Both query parameters and virtual subdirectories (path segments) are susceptible to duplicate content issues if not managed appropriately; canonicalizing URLs can help avoid duplicate content penalties.&lt;/p&gt;

&lt;p&gt;To determine which URL structure is best for SEO, let’s weigh the pros and cons of each option according to the “best and worst” practices of faceted navigation as defined by Google.&lt;/p&gt;

&lt;h2 id=&quot;uniqueness&quot;&gt;Uniqueness&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;In an ideal state, unique content – whether an individual product/article or a category of products/articles – would have only one accessible URL.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As it pertains to URL uniqueness, neither query parameters nor virtual subdirectories are superior.&lt;/p&gt;

&lt;p&gt;Google treats each distinct URL (including those with query parameters) as a unique URL with unique content. This is because it is technically possible for a website to serve different content at each distinct URL, and for URLs that include query parameters, it is likely that content may change based on these values.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Tip:&lt;/strong&gt; This also applies to &lt;code class=&quot;highlighter-rouge&quot;&gt;http&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;https&lt;/code&gt; URLs and URLs that can be visited with and without a subdomain (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;www&lt;/code&gt;); canonicalizing or redirecting these URLs will help avoid duplicate content penalties.&lt;/p&gt;

&lt;h2 id=&quot;multiple-selection&quot;&gt;Multiple Selection&lt;/h2&gt;

&lt;p&gt;One challenge faced when structuring faceted URLs is correctly denoting taxonomy for multiple values and multiple options. As more facets are added, the URL structure becomes increasingly complicated. The following examples illustrate two common approaches to this problem:&lt;/p&gt;

&lt;ol class=&quot;u-break-word&quot;&gt;
  &lt;li&gt;Query parameters
    &lt;ul&gt;
      &lt;li&gt;https://example.com/category?color=black,white&lt;/li&gt;
      &lt;li&gt;https://example.com/category?color=black,white&amp;amp;size=8,10&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Virtual subdirectories
    &lt;ul&gt;
      &lt;li&gt;https://example.com/category/filter/color/black,white/&lt;/li&gt;
      &lt;li&gt;https://example.com/category/filter/color/black,white/size/8,10/&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These approaches raise concerns about valid URL encoding.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;… [The danger of hierarchical classification as a general solution lies] in the philosophy of meaning. … Because the relationships between subjects are web-like rather than tree-like, even for people who agree on a web may pick a different tree representation.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://www.w3.org/Provider/Style/URI.html&quot;&gt;Cool URIs don’t change&lt;/a&gt;, Tim Berners-Lee&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;non-standard-url-encoding&quot;&gt;Non-Standard URL Encoding&lt;/h3&gt;

&lt;p&gt;Google advises against using “non-standard URL encoding for parameters… instead of &lt;code class=&quot;highlighter-rouge&quot;&gt;key=value&amp;amp;&lt;/code&gt; pairs.”&lt;/p&gt;

&lt;p&gt;Google provides two “worst practice” examples where key-value pairs are marked incorrectly with &lt;code class=&quot;highlighter-rouge&quot;&gt;:&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;,&lt;/code&gt; rather than &lt;code class=&quot;highlighter-rouge&quot;&gt;=&lt;/code&gt;, and where &lt;em&gt;multiple parameters are appended&lt;/em&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;[]&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;,,&lt;/code&gt; rather than &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If key-value pairs are marked correctly, there are valid special characters that may be used within a URL.&lt;/p&gt;

&lt;p&gt;The comma, for example, is an allowed path character (i.e. “pchar”) as part of the sub delimiters (i.e. “sub-delims”) defined in &lt;a href=&quot;https://tools.ietf.org/html/rfc3986#section-2.2&quot;&gt;RFC 3986 § 2.2&lt;/a&gt; for &lt;em&gt;both&lt;/em&gt; query parameters &lt;em&gt;and&lt;/em&gt; path segments. As &lt;a href=&quot;http://tools.ietf.org/html/rfc3986#section-3.3&quot;&gt;RFC 3986 § 3.3&lt;/a&gt; states:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;the semicolon (“;”) and equals (“=”) reserved characters are often used to delimit parameters and parameter values applicable to that segment. The comma (“,”) reserved character is often used for similar purposes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While these characters may be used in both path and query segments, it is uncommon to see these characters in path segments because delimiting options in a path segment &lt;em&gt;does not impart a clear hierarchy&lt;/em&gt;.&lt;/p&gt;

&lt;h3 id=&quot;are-facets-hierarchical&quot;&gt;Are Facets Hierarchical?&lt;/h3&gt;

&lt;p&gt;The heart of the question is whether or not facets are hierarchical data.&lt;/p&gt;

&lt;p&gt;According to the &lt;a href=&quot;https://en.wikipedia.org/wiki/Faceted_classification#Comparison_between_faceted_and_single_hierarchical_classification&quot;&gt;Wikipedia entry on “Faceted classification”&lt;/a&gt;, facets are &lt;strong&gt;not&lt;/strong&gt; hierarchical:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Hierarchical classification refers to the classification of objects using &lt;em&gt;one single hierarchical taxonomy&lt;/em&gt;. Faceted classification may actually &lt;em&gt;employ hierarchy&lt;/em&gt; in one or more of its facets, &lt;em&gt;but allows for the use of more than one taxonomy&lt;/em&gt; to classify objects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;As we’ve seen in our examples above, the “multiple taxonomies” presented by facets is not well suited to inclusion in the path segment of the URL. Returning to the point of using non-standard URL encoding, according to &lt;a href=&quot;http://tools.ietf.org/html/rfc3986#section-3.3&quot;&gt;RFC 3986 § 3.3&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The path component contains data, usually organized in hierarchical form, that, along with data in the non-hierarchical query component (&lt;a href=&quot;http://tools.ietf.org/html/rfc3986#section-3.4&quot;&gt;Section 3.4&lt;/a&gt;), serves to identify a resource…&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href=&quot;http://tools.ietf.org/html/rfc3986#section-3.4&quot;&gt;RFC 3986 § 3.4&lt;/a&gt; continues:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The query component contains non-hierarchical data&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This makes it clear that facets should not appear in path segments, but should appear as query parameters. In fact, Google instructs:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Use &lt;em&gt;parameters&lt;/em&gt; (when possible) with standard encoding and key=value pairs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using virtual subdirectories to denote facets is non-standard and Google is able to make better assumptions of page contents when facets are conveyed through query parameters. Google even provides a &lt;a href=&quot;https://support.google.com/webmasters/answer/6080550?hl=en&quot;&gt;URL Parameters tool&lt;/a&gt; in Google Search Console that allows site administrators to instruct Google on how to interpret query parameters; no such tool exists for virtual subdirectories.&lt;/p&gt;

&lt;div class=&quot;placeholder screenshot-window&quot; style=&quot;--aspect-ratio:calc(100%/(850/600))&quot;&gt;
  &lt;div style=&quot;max-width:850px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Screenshot of Google Search Console's URL Parameters tool&quot; width=&quot;850&quot; height=&quot;600&quot; integrity=&quot;sha256-SQCqYWz4Oo+4TQLVyH5KeoTmgngeyneeg5IUfCOIGOo=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/gsc-url-parameters-4900aa616cf83a8fb84d02d5c87e4a7a84e682781eca779e8392147c238818ea.png&quot; /&gt;
&lt;/div&gt;

&lt;h2 id=&quot;immutable-values&quot;&gt;Immutable values&lt;/h2&gt;

&lt;p&gt;Another “worst practice” as defined by Google is “using directories or file paths rather than parameters to list values that don’t change page content.”&lt;/p&gt;

&lt;p&gt;In a directory based faceted URL structure, the following URLs would all serve the same content:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;https://example.com/category/filter/option/&lt;/li&gt;
  &lt;li&gt;https://example.com/category/filter/&lt;/li&gt;
  &lt;li&gt;https://example.com/category/&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This issue is less apparent when using query parameters because there is a clear delineation between hierarchy and facets using key-value pairs. This issue may be resolved via canonicalization, but is not considered best practice as noted &lt;a href=&quot;https://webmasters.googleblog.com/2014/02/faceted-navigation-best-and-5-of-worst.html#worst-practices&quot;&gt;by Google&lt;/a&gt;; best practice is to use query parameters, because “URL parameters allow more flexibility for search engines to determine how to crawl efficiently.”&lt;/p&gt;

&lt;h2 id=&quot;facet-order&quot;&gt;Facet Order&lt;/h2&gt;

&lt;p&gt;Regardless of which URL structure is used, facets should always be presented in a unified manner (e.g. alphabetical order), so multiple URLs are not indexed for the same content. Take the following URLs as an example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;https://example.com/category?test=0,1&lt;/li&gt;
  &lt;li&gt;https://example.com/category?test=1,0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both of these URLs would display the same content. To reduce the total number of unique links on a site, and thus duplicate content from being indexed, only one of the above should be used consistently across a site.&lt;/p&gt;

&lt;p&gt;Again, redirection or canonicalization can help search engines index this content correctly if it is referenced elsewhere.&lt;/p&gt;

&lt;h2 id=&quot;user-experience&quot;&gt;User Experience&lt;/h2&gt;

&lt;p&gt;As a front-facing component of websites, URLs are an important part of the user experience. The URL acts as a reference point for the current view, and advanced users may use the URL as a “virtual breadcrumb trail” to navigate backwards through your site’s hierarchy. Maintaining a human parsible URL is an important and non-trivial endeavor.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; I’ve written &lt;a href=&quot;/the-url-is-dead-long-live-the-url-8e87de87&quot;&gt;an extensive post on the importance of designing URLs as part of a site’s UI&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;logical-additions&quot;&gt;Logical Additions&lt;/h3&gt;

&lt;p&gt;Google advises against “appending URL parameters without logic”. Unnecessary parameters should be stripped to maintain a human parsible URL structure, when possible. Google recommends removing user session information from the URL and storing that data in cookies instead. Keeping the URL free of unnecessary data not only helps users understand the content present in the current view, it also aides SEO, as Google notes:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Extraneous URL parameters only increase duplication, causing less efficient crawling and indexing.&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>Good UX Is Good SEO</title>
   <link href="https://joeyhoer.com/good-ux-is-good-seo-0b50ea0b"/>
   <updated>2016-10-05T22:47:50-04:00</updated>
   <id>https://joeyhoer.com/good-ux-is-good-seo</id>
   <content type="html">&lt;p&gt;Google has a long standing tradition of encouraging a better web by updating its algorithms to benefit sites that offer a positive user experience (UX) and penalize those that don’t. As Google Webmaster &lt;a href=&quot;https://www.youtube.com/watch?v=jGjGtOKJTLQ&quot;&gt;Matt Cutts explains&lt;/a&gt;, “we try to help people make the web a better experience, so people will be on the web longer, and people will be happier … for example, we never show popup ads on Google. Even though it might have meant a little bit more money up front, because we also knew &lt;strong&gt;it would also annoy users and make them less likely to come back&lt;/strong&gt;.”&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Here are a few examples of Google algorithm updates that have focused on making the web a better place:&lt;/p&gt;

&lt;h3 id=&quot;site-speed&quot;&gt;Site Speed&lt;/h3&gt;

&lt;p&gt;In August 2010 Google introduced the site speed ranking signal and “encouraged [site owners] to start looking at site speed — not only to improve ranking in search engines, but also to &lt;strong&gt;improve everyone’s experience&lt;/strong&gt; on the Internet.”&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Faster sites create &lt;strong&gt;happy users&lt;/strong&gt; and we’ve seen in our internal studies that when a site responds slowly, visitors spend less time there. But faster sites don’t just improve &lt;strong&gt;user experience&lt;/strong&gt;; recent data shows that improving site speed also reduces operating costs.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2010/04/using-site-speed-in-web-search-ranking.html&quot;&gt;Using site speed in web search ranking&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In 2018, Google doubled down on site speed with “Speed Update”, making page speed a ranking factor for mobile searches—which are now &lt;a href=&quot;https://webmasters.googleblog.com/2016/11/mobile-first-indexing.html&quot;&gt;the primary source of site rank&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;People want to be able to find answers to their questions as fast as possible — studies show that &lt;strong&gt;people really care&lt;/strong&gt; about the speed of a page. … We encourage developers to think broadly about how performance affects &lt;strong&gt;a user’s experience&lt;/strong&gt; of their page and to consider a variety of user experience metrics.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;pagespeed-insights&quot;&gt;PageSpeed Insights&lt;/h3&gt;

&lt;p&gt;In May 2014 Google’s PageSpeed Insights was updated to offer suggestions for improving user experience.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Poor usability&lt;/strong&gt; can diminish the benefits of a fast page load.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2014/05/making-your-site-more-mobile-friendly.html&quot;&gt;Making your site more mobile-friendly with PageSpeed Insights&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;modern-technology&quot;&gt;Modern Technology&lt;/h3&gt;

&lt;p&gt;In July 2014 Google began displaying search result indicators for pages that used unsupported technology (i.e. Flash).&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A &lt;strong&gt;common annoyance&lt;/strong&gt; for web users is when websites require browser technologies that are not supported by their device.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2014/07/promoting-modern-websites-for-modern.html&quot;&gt;Promoting modern websites for modern devices in Google search results&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;https&quot;&gt;HTTPS&lt;/h3&gt;

&lt;p&gt;In August 2014 Google began using HTTPS as a ranking signal, and in December 2015 Google began crawling and indexing HTTPS pages by default, even for HTTPS URLs that were not linked elsewhere.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;At Google, user security has always been a top priority. Over the years, we’ve worked hard to promote a more secure web and to provide &lt;strong&gt;a better browsing experience for users&lt;/strong&gt;. …we started giving a slight ranking boost to HTTPS URLs in search results last year. Browsing the web should be a private experience between the user and the website, and must not be subject to eavesdropping, man-in-the-middle attacks, or data modification. This is why we’ve been strongly promoting HTTPS everywhere.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2015/12/indexing-https-pages-by-default.html&quot;&gt;Indexing HTTPS pages by default&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;We’d like to encourage all website owners to switch from HTTP to HTTPS &lt;strong&gt;to keep everyone safe on the web&lt;/strong&gt;.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2014/08/https-as-ranking-signal.html&quot;&gt;HTTPS as a raking signal&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;mobile-friendly&quot;&gt;Mobile Friendly&lt;/h3&gt;

&lt;p&gt;In Febuary 2015 Google &lt;a href=&quot;https://webmasters.googleblog.com/2015/02/finding-more-mobile-friendly-search.html&quot;&gt;announced the mobile-friendly update&lt;/a&gt; and in April 2015 &lt;a href=&quot;https://webmasters.googleblog.com/2015/04/rolling-out-mobile-friendly-update.html&quot;&gt;the new signal was introduced&lt;/a&gt;, boosting the ranking of pages using responsive web design.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The desktop version of a site might be difficult to view and use on a mobile device. The version that’s not mobile-friendly requires the user to pinch or zoom in order to read the content. Users find this a &lt;strong&gt;frustrating experience&lt;/strong&gt; and are likely to abandon the site. Alternatively, the mobile-friendly version is readable and immediately usable.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://developers.google.com/webmasters/mobile-sites/get-started/why&quot;&gt;Mobile Friendly Websites&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;mobile-first-indexing&quot;&gt;Mobile-first Indexing&lt;/h3&gt;

&lt;p&gt;In November 2016 Google &lt;a href=&quot;https://webmasters.googleblog.com/2016/11/mobile-first-indexing.html&quot;&gt;announced that a new “mobile index” would become the primary source of site rank&lt;/a&gt;, further benefiting sites with a well optimized mobile experience.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Today, most people are searching on Google using a mobile device. … To make our results more useful, we’ve begun experiments to make our index mobile-first. … Of course, while our index will be built from mobile documents, we’re going to continue to build &lt;strong&gt;a great search experience&lt;/strong&gt; for all users, whether they come from mobile or desktop devices.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;doorway-pages&quot;&gt;Doorway Pages&lt;/h3&gt;

&lt;p&gt;In March 2015 Google announced a ranking adjustment to better address &lt;a href=&quot;https://support.google.com/webmasters/answer/2721311&quot;&gt;doorway pages&lt;/a&gt; – sites or pages created to rank highly for specific search queries – as part of a continual effort by Google’s Search Quality team to “minimize the impact of webspam on users.”&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[I]f a user clicks on one result, doesn’t like it, and then tries the next result in the search results page and is taken to that same site that they didn’t like, that’s a really &lt;strong&gt;frustrating experience&lt;/strong&gt;.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2015/03/an-update-on-doorway-pages.html&quot;&gt;An update on doorway pages&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;app-pop-overs&quot;&gt;App Pop Overs&lt;/h2&gt;

&lt;p&gt;In September 2015 Google announced a change to their mobile-friendly algorithms to exclude web pages that show an app install interstitial that hides a significant amount of content.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Sometimes a user may tap on a search result on a mobile device and see an app install interstitial that hides a significant amount of content and prompts the user to install an app. Our analysis shows that it is not a good search experience and can be &lt;strong&gt;frustrating for users&lt;/strong&gt; because they are expecting to see the content of the web page.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2015/09/mobile-friendly-web-pages-using-app.html&quot;&gt;Mobile-friendly web pages using app banners&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;[A] full page interstitial can interrupt the user from reaching their desired content.
…Our analysis found that 69% of the visits abandoned [on our interstitial] page.
…Based on these results, we decided to permanently retire the interstitial …and we are sharing this with the hope that you will reconsider the use of promotional interstitials. &lt;strong&gt;Let’s remove friction and make the mobile web more useful and usable!&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2015/07/google-case-study-on-app-download-interstitials.html&quot;&gt;Google+: A case study on App Download Interstitials&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;interstitials&quot;&gt;Interstitials&lt;/h2&gt;

&lt;p&gt;In August 2016 Google announced a search penalty for pages where content is not easily accessible to a user due to “intrusive interstitials”.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[C]ontent may be visually obscured by an interstitial. This can &lt;strong&gt;frustrate users&lt;/strong&gt; because they are unable to easily access the content that they were expecting…&lt;/p&gt;

  &lt;p&gt;Pages that show intrusive interstitials provide a &lt;strong&gt;poorer experience to users&lt;/strong&gt; than other pages where content is immediately accessible.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2016/08/helping-users-easily-access-content-on.html&quot;&gt;Helping users easily access content on mobile&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content>
 </entry>
 
 <entry>
   <title>The Appropriate Use of Modals</title>
   <link href="https://joeyhoer.com/the-appropriate-use-of-modals-f44f6c11"/>
   <updated>2016-08-24T02:48:23-04:00</updated>
   <id>https://joeyhoer.com/the-appropriate-use-of-modals</id>
   <content type="html">&lt;p&gt;Modal windows are designed to &lt;em&gt;enhance user experience&lt;/em&gt; by presenting new content &lt;em&gt;situationally&lt;/em&gt;. By &lt;a href=&quot;http://en.wikipedia.org/wiki/Modal_window&quot;&gt;definition&lt;/a&gt;, these interstitials obstruct user flow by preventing interaction with the main application. This UI element generally requires a user interaction to initiate its display; however, a page load, scroll event, or an arbitrary timeout is not a justifiable “interaction” for the presentation of a modal window. Presenting a modal window without an appropriate prequalifying event not only disrupts user flow and concentration, it also introduces frustration and confusion.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;those-who-forget-history&quot;&gt;Those who forget history…&lt;/h2&gt;

&lt;p&gt;In the early day’s of the web, pop-up advertisements plagued web surfers. The problem became so prolific that browser vendors were forced (by user demand) to implement pop-up blockers to protect users from intrusive advertising aimed at cheap conversion.&lt;/p&gt;

&lt;div class=&quot;placeholder screenshot-window&quot; style=&quot;--aspect-ratio:calc(100%/(400/272))&quot;&gt;
  &lt;div style=&quot;max-width:400px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Screenshot of Popup Blocker in Chrome 52 on macOS&quot; width=&quot;400&quot; height=&quot;272&quot; integrity=&quot;sha256-VHgG0j0y9JSOhwru6u5Gw291hKj9HwveVizfBIQp6Ac=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/chrome-popup-blocked-547806d23d32f4948e870aeeeaee46c36f7584a8fd1f0bde562cdf048429e807.png&quot; /&gt;
&lt;/div&gt;

&lt;p class=&quot;note&quot;&gt;&lt;a href=&quot;http://website-archive.mozilla.org/www.mozilla.org/firefox_releasenotes/en-US/firefox/releases/0.1.html&quot;&gt;Firefox 0.1&lt;/a&gt; (originally named Phoenix), released September 23, 2002, introduced the first pop-up blocker. Shortly afterwards, on November 13, 2002, &lt;a href=&quot;http://www.opera.com/docs/changelogs/windows/700b1/&quot;&gt;Opera 7.0 Beta 1&lt;/a&gt; followed suit. By August 2004, all major browsers included built-in pop-up blockers. These built-in pop-up blockers still exist today.&lt;/p&gt;

&lt;p&gt;Matt Korostoff recounts the overuse of pop-up windows for advertisements during the Internet boom:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;There was a time when any web page could pop open as many windows as it wanted without your consent… popup blockers became so ubiquitous and effective that now advertisers hardly bother with them anymore.&lt;/p&gt;

  &lt;p&gt;Let us not forget, old-style &lt;code class=&quot;highlighter-rouge&quot;&gt;window.open()&lt;/code&gt; popups weren’t originally invented to display ads. They’ve been largely eliminated, not because they stopped being a useful, but simply because advertisers used them with such abandon that the rest of the web was becoming unusable. &lt;strong&gt;We broke that feature of the web because we couldn’t be trusted to use it responsibly.&lt;/strong&gt;&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;http://mattkorostoff.com/article/popup-ads-are-back&quot;&gt;Popup ads are back&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The prosaic pop-up window has been replaced by the modern modal, reintroducing the frustration and confusion of the Y2K era to today’s users. In many ways, the modal is more contemptible than the pop-up. The pop-up, while relentless, does not directly prevent a user from interacting with an application; modals, on the other hand, actively block users from accessing information and content. The modal also typically demands a higher level of engagement than a simple click, despite not having established &lt;strong&gt;prerequisite trust&lt;/strong&gt;, and – if not implemented correctly – presents a number of accessibility concerns.&lt;/p&gt;

&lt;h1 id=&quot;modals-are-popups&quot;&gt;Modals are popups&lt;/h1&gt;

&lt;video autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot; poster=&quot;/assets/ace-on-modals-poster-15823c75cb09b8073fee1a88e7d1a3c3fa28ca77dbe5d477f12d0eeff1cd96c6.jpg&quot; style=&quot;max-height:300px&quot;&gt;
  &lt;source src=&quot;/assets/ace-on-modals-ccf6312508ea6a962dfc7dd120e447a35e9ae93438804e6657b9e0897bc5048d.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;

&lt;p&gt;Modals (i.e. pop-overs, pop-ups, or &lt;a href=&quot;https://en.wikipedia.org/wiki/Interstitial_webpage&quot;&gt;interstitials&lt;/a&gt;) have become increasingly used to coerce unsuspecting users into conversions. This erodes user trust and leads to a poor overall experience (often initiated during the user’s introduction to a site).&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;Using the word “modal” is actually an inaccurate description of the common form most prevalent on the web today. A more appropriate term is “interstitial”, which describes a barrier placed before the expected content, and sits – literally – “in between” the user and the desired content.&lt;/p&gt;

&lt;h3 id=&quot;content-barriers&quot;&gt;Content Barriers&lt;/h3&gt;

&lt;p&gt;Undoubtedly, the most important aspect of a site – to a visitor – is the content the site contains. Interstitials block this content by forcing the user to interact with an unexpected interruption. This diverts the visitors attention away from their goal, and ultimately, away from any conversion opportunities on the site.&lt;/p&gt;

&lt;p&gt;This may seem like a minor inconvenience, but as Jakob Nielsen reports, “annoyances matter, because they compound.” This is particularly true if a user’s first experience with a site is negative.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Even if no single annoyance stops users in their tracks or makes them leave the site, the combined negative impact of the annoyances will make users feel less satisfied. … [E]liminating annoyances increases customer satisfaction and user loyalty, and thus improves the long-term business value of the site.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;http://www.nngroup.com/articles/does-user-annoyance-matter/&quot;&gt;Does User Annoyance Matter?&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is particularly relevant for ecommerce sites where the main goal is to convert users as quickly as possible, as Telegraph senior designer Andy Booth explains.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If [I] could only focus on one thing to improve conversions… [it would be]
the [ecommerce] landing page. The average user will make up their mind if
they trust your website within three seconds at best – so you better hope
your page is fully loaded and shows off what your trying to sell. Don’t hide
it behind unnecessary distractions or email signups… If you are an unknown
site, you will be judged on how trustworthy you are.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://twitter.com/theandybooth&quot;&gt;Andy Booth&lt;/a&gt; Net Magazine, Issue 278, April 2016 Page 12&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Obstructing content not only presents usability concerns by frustrating users and needlessly delaying conversion, it also erodes user trust.&lt;/p&gt;

&lt;h3 id=&quot;trust-and-reciprocity&quot;&gt;Trust and Reciprocity&lt;/h3&gt;

&lt;p&gt;When aspiring to “true conversion”, trust is extremely important. Trust may be the most valuable commodity of the modern web. You can’t ask someone for their trust, you must earn it, and once you have earned a user’s trust, you gain privileges others don’t.&lt;/p&gt;

&lt;p&gt;The Nielsen Norman Group has performed in-depth analysis of trust and user commitment:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The more information or effort a site asks for, the more trust and comfort the user must have. In our rush to collect and convert… users get put off and abandon the site because [we haven’t] covered the basic levels of commitment.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://www.nngroup.com/articles/commitment-levels/&quot;&gt;Hierarchy of Trust: The 5 Experiential Levels of Commitment&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Requesting a user’s information before offering the user any substantial value contributes to an overall negative impression of a brand. If you do collect a user’s information, think of creative ways to offer the user something in return – perhaps by auto-populating other forms with the information you’ve collected.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you want your users to trust you with their information and come back to you repeatedly, plant the reciprocity seed by being nice to them upfront and minimizing their interaction cost. Ask as little of your users as possible.  On the web and elsewhere, start by giving before taking, and people will reciprocate.&lt;/p&gt;

  &lt;p&gt;&lt;a href=&quot;https://www.nngroup.com/articles/reciprocity-principle/&quot;&gt;The Reciprocity Principle: Give Before You Take in Web Design&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Asking a user for data is one thing, tricking a user into providing data is another. Interstitials, depending upon the implementation, walk a line of deception by presenting accessibility concerns.&lt;/p&gt;

&lt;h1 id=&quot;accessibility-concerns&quot;&gt;Accessibility Concerns&lt;/h1&gt;

&lt;p&gt;Interstitials are difficult to execute properly without affecting accessibility in some way. Users with visual or cognitive impairments may struggle with interstitials, and may not realize or understand how to dismiss an interstitial. This experience can force users into divulging more information than they feel comfortable with, and may cause users to abandon a site.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://darkpatterns.org&quot;&gt;Dark patterns&lt;/a&gt; are insincere and deceptive user interface elements designed to coerce users into performing an action unintentionally. Interstitials can present many dark patterns including “forced disclosure”, “privacy zuckering” and “road blocking”&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Prioritizing conversions or short-term metrics leads designers to pressure people into doing things they don’t actually want to do and can easily cross the ethical boundaries towards dark patterns. It’s time to reassess priorities and long-term goals: you may be getting a few extra clicks now, but in the long run you’re losing your users’ trust and respect.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://www.nngroup.com/articles/needy-design-patterns/&quot;&gt;Needy Design Patterns: Please-Don’t-Go Popups &amp;amp; Get-Back-to-Me Tabs&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;user-feedback&quot;&gt;User Feedback&lt;/h2&gt;

&lt;p&gt;Users find modals so aggravating, in fact, that entire websites have been devoted to users who eschew modals:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://tabcloseddidntread.com&quot;&gt;Tab Closed; Didn’t Read&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://idontwantyourfuckingapp.tumblr.com&quot;&gt;I Don’t Want Your &amp;amp;?@#!#%ing App&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Users are actively declaring that they abandon sites which present these distractions, but as the old user experience axiom goes “watch what people do, not what they say.”&lt;/p&gt;

&lt;h2 id=&quot;seo-impact&quot;&gt;SEO Impact&lt;/h2&gt;

&lt;p&gt;Google arguably has access to the most data on how technology and user experience affect user engagement; while no one but Google knows exactly how many websites use Google Analytics, &lt;a href=&quot;http://trends.builtwith.com/analytics/Google-Analytics&quot;&gt;sources&lt;/a&gt; &lt;a href=&quot;https://w3techs.com/technologies/overview/traffic_analysis/all&quot;&gt;estimate&lt;/a&gt; that Google Analytics is installed on 55–75% of all websites. Google has a vested interest in making the web more usable and less confusing for users, because the faster the web is and the more satisfied Google users are with the results, the more pages they will view and the more ads Google will get to display.&lt;/p&gt;

&lt;p&gt;However Google does not allow their marketing efforts to obstruct the user’s goals, as Google Webmaster &lt;a href=&quot;https://www.youtube.com/watch?v=jGjGtOKJTLQ&quot;&gt;Matt Cutts explains&lt;/a&gt;, “we try to help people make the web a better experience, so people will be on the web longer, and people will be happier … for example, we &lt;strong&gt;never show popup ads on Google&lt;/strong&gt;. Even though it might have meant a little bit more money up front, because we also knew &lt;strong&gt;it would also annoy users and make them less likely to come back&lt;/strong&gt;.”&lt;/p&gt;

&lt;p&gt;I’ve written about &lt;a href=&quot;/good-ux-is-good-seo/#interstitals&quot;&gt;good UX being good SEO&lt;/a&gt;, and Google seems to agree with me.&lt;/p&gt;

&lt;p&gt;Google has instituted changes to block technology similar to interstitials in the past, banning doorway pages in March 2015, and blocking “app interstitials” in September 2015 because they were “frustrating users”, and in August 2016 Google announced a search penalty for pages where content is not easily accessible to a user due to “intrusive interstitials”:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[C]ontent may be visually obscured by an interstitial. This can &lt;strong&gt;frustrate users&lt;/strong&gt; because they are unable to easily access the content that they were expecting…&lt;/p&gt;

  &lt;p&gt;Pages that show intrusive interstitials provide a &lt;strong&gt;poorer experience to users&lt;/strong&gt; than other pages where content is immediately accessible.&lt;/p&gt;

  &lt;p&gt;Here are some examples of techniques that make content less accessible to a user:&lt;/p&gt;

  &lt;ul&gt;
    &lt;li&gt;Showing a popup that covers the main content, either immediately after the user navigates to a page from the search results, or while they are looking through the page.&lt;/li&gt;
    &lt;li&gt;Displaying a standalone interstitial that the user has to dismiss before accessing the main content.&lt;/li&gt;
    &lt;li&gt;Using a layout where the above-the-fold portion of the page appears similar to a standalone interstitial, but the original content has been inlined underneath the fold.&lt;/li&gt;
  &lt;/ul&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2016/08/helping-users-easily-access-content-on.html&quot;&gt;Helping users easily access content on mobile&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The only true way to assess the impact of an interstitial (and results will vary) is to measure user interaction and conversion for users that have been presented with an interstitial.&lt;/p&gt;

&lt;h2 id=&quot;an-empirical-study&quot;&gt;An Empirical Study&lt;/h2&gt;

&lt;p&gt;If you plan on implementing an interstitial, I suggest using the following outline for an empirical study on the effectiveness of the modal:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Document how the modal functions, and include the following information:
    &lt;ul&gt;
      &lt;li&gt;How and when the modal is presented to a user&lt;/li&gt;
      &lt;li&gt;Methods in which focus is drawn to the modal&lt;/li&gt;
      &lt;li&gt;Methods with which the modal may be dismissed (&lt;code class=&quot;highlighter-rouge&quot;&gt;ESC&lt;/code&gt; key, clicking the background, an obvious escape/close button, etc.)&lt;/li&gt;
      &lt;li&gt;Clarity of exit actions&lt;/li&gt;
      &lt;li&gt;How and when the modal may be reintroduced&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Gather metrics on the modal usage and performance
    &lt;ul&gt;
      &lt;li&gt;Discover which “exit action” is used most frequently&lt;/li&gt;
      &lt;li&gt;A/B test the modal for abandonment, sign-up rates&lt;/li&gt;
      &lt;li&gt;Test all modal “conversions” for engagement and future conversions&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Perform user testing and collect real user comments&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h2&gt;

&lt;blockquote&gt;
  &lt;p&gt;Often these types of overlays are deliberate attempts to entice the user to
take some action that benefits the organization (such as subscribing to a
newsletter). Many web marketers seem to operate under the ‘ends justify
the means’ principle, and believe that the benefit of increasing your
subscriber base outweighs the negative consequences of annoying your users.
If you attempt this strategy, make sure to follow up and measure whether
those extra subscribers are actually qualified leads. Do they really end up
buying your products? If not, you may be aggravating your true audience for
no reason at all.&lt;/p&gt;

  &lt;p&gt;… Don’t use an overlay unless you have a clear, compelling case for why this
content should not be presented within a regular page.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;http://www.nngroup.com/articles/overuse-of-overlays/&quot;&gt;Overuse of Overlays: How to Avoid Misusing Lightboxes&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every time you make a change, ask yourself, who will this change benefit? Does this change benefit you, as a business owner, or your users? If the answer doesn’t include the user, you should reconsider.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;

      &lt;dl class=&quot;compact&quot;&gt;
        &lt;dt&gt;Forced Disclosure&lt;/dt&gt;
        &lt;dd&gt;In return for a free or low-cost action, the site requires the user to disclose extensive personal information – unnecessary to the transaction in-hand.&lt;/dd&gt;
        &lt;dt&gt;Privacy Zuckering&lt;/dt&gt;
        &lt;dd&gt;“The act of creating deliberately confusing jargon and user-interfaces which trick your users into sharing more info about themselves than they really want to.” (As defined by the EFF).&lt;/dd&gt;
        &lt;dt&gt;Road Blocking&lt;/dt&gt;
        &lt;dd&gt;When the user’s progress to task completion is restricted or stopped by something else on the screen.&lt;/dd&gt;
      &lt;/dl&gt;
      &lt;p&gt;&lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Designing & Optimizing Favicons</title>
   <link href="https://joeyhoer.com/optimizing-favicons-612d96c6"/>
   <updated>2016-08-13T14:08:06-04:00</updated>
   <id>https://joeyhoer.com/optimizing-favicons</id>
   <content type="html">&lt;p&gt;Favicons, or favorites icons, provide users a visual way to differentiate between a browser’s tabs, bookmarks, favorites, and history items. Depending on browsing habits, a user may have more exposure to a website’s favicon than its logo. To craft a favicon that improves user experience and entices users to return to a site, authors should ensure that the favicon is unique, crisp and well optimized.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;browser-support&quot;&gt;Browser Support&lt;/h2&gt;

&lt;p&gt;Modern browsers support a variety of file formats to be used as favicons. The most commonly used file types are &lt;code class=&quot;highlighter-rouge&quot;&gt;PNG&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;GIF&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt;, though &lt;code class=&quot;highlighter-rouge&quot;&gt;JPG&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;APNG&lt;/code&gt;, and &lt;code class=&quot;highlighter-rouge&quot;&gt;SVG&lt;/code&gt; may also be supported depending upon the browser. Each file type has it’s own distinct advantages and disadvantages.&lt;/p&gt;

&lt;table class=&quot;flipped-axis striped&quot;&gt;
  &lt;thead&gt;
    &lt;th&gt;&lt;/th&gt;
    &lt;th&gt;Transparency&lt;/th&gt;
    &lt;th&gt;Animation&lt;/th&gt;
    &lt;th&gt;Multiple Image&lt;/th&gt;
    &lt;th&gt;Compression&lt;/th&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;JPEG&lt;/th&gt;
      &lt;td class=&quot;align-center false&quot;&gt;✗&lt;/td&gt;
      &lt;td class=&quot;align-center false&quot;&gt;✗&lt;/td&gt;
      &lt;td class=&quot;align-center false&quot;&gt;✗&lt;/td&gt;
      &lt;td class=&quot;align-center true&quot;&gt;✓&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;GIF&lt;/th&gt;
      &lt;td class=&quot;align-center true&quot;&gt;✓&lt;/td&gt;
      &lt;td class=&quot;align-center true&quot;&gt;✓&lt;/td&gt;
      &lt;td class=&quot;align-center false&quot;&gt;✗&lt;/td&gt;
      &lt;td class=&quot;align-center true&quot;&gt;✓&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;PNG&lt;/th&gt;
      &lt;td class=&quot;align-center true&quot;&gt;✓&lt;/td&gt;
      &lt;td class=&quot;align-center false&quot;&gt;✗&lt;/td&gt;
      &lt;td class=&quot;align-center false&quot;&gt;✗&lt;/td&gt;
      &lt;td class=&quot;align-center true&quot;&gt;✓&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;ICO&lt;/th&gt;
      &lt;td class=&quot;align-center true&quot;&gt;✓&lt;/td&gt;
      &lt;td class=&quot;align-center false&quot;&gt;✗&lt;/td&gt;
      &lt;td class=&quot;align-center true&quot;&gt;✓&lt;/td&gt;
      &lt;td class=&quot;align-center false&quot;&gt;✗&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; format is ideal for modern favicons because it allows authors to embed multiple images in a single file, allowing different icon sizes to be used in different contexts, and supports &lt;strong&gt;full alpha transparency&lt;/strong&gt;. Additionally, authors &lt;strong&gt;are not required to implement any code on a page&lt;/strong&gt; to use an &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; formatted favicon.&lt;/p&gt;

&lt;p&gt;While &lt;code class=&quot;highlighter-rouge&quot;&gt;JPG&lt;/code&gt; favicons are supported in most browsers, this format should not be used because it does not support transparency, and its lossy compression is less than ideal for small icons.&lt;/p&gt;

&lt;p&gt;Developers may choose to use the &lt;code class=&quot;highlighter-rouge&quot;&gt;GIF&lt;/code&gt; format to encode favicons because the format supports animation, &lt;strong&gt;indexed transparency&lt;/strong&gt;, and generally offers the smallest file size (at &lt;code class=&quot;highlighter-rouge&quot;&gt;16px&lt;/code&gt;). However, because index transparency is binary (either the pixel is fully transparent or fully opaque), the format often produces undesirable visual artifacts, unless the favicon can map directly to a pixel grid and does not require the use of semi-transparent pixels. Likewise, animation is rarely necessary or desirable in a favicon.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;PNG&lt;/code&gt; format overcomes the limitations of &lt;code class=&quot;highlighter-rouge&quot;&gt;GIF&lt;/code&gt; by offering &lt;strong&gt;full alpha transparency&lt;/strong&gt; and a superior compression algorithm that can produce much better visual results.&lt;/p&gt;

&lt;p&gt;Until &lt;code class=&quot;highlighter-rouge&quot;&gt;SVG&lt;/code&gt; favicons are widely supported, authors should use the &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; format and embed (at least) &lt;code class=&quot;highlighter-rouge&quot;&gt;16px&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;32px&lt;/code&gt; sizes.&lt;/p&gt;

&lt;h2 id=&quot;history-of-the-ico-format&quot;&gt;History of the &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; format&lt;/h2&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; format, developed by Microsoft, acts as a wrapper for &lt;strong&gt;bitmapped images&lt;/strong&gt;. In other words, &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; files &lt;em&gt;are not intrinsically compressed&lt;/em&gt;. In 2007, with the release of Windows Vista, the &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; format was updated to support embedding of compressed &lt;code class=&quot;highlighter-rouge&quot;&gt;PNG&lt;/code&gt; contents (though I’ve only ever had success embedding compressed content at &lt;code class=&quot;highlighter-rouge&quot;&gt;256px&lt;/code&gt;). As such, authors should ensure that &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; files are compressed and served with a compressed &lt;code class=&quot;highlighter-rouge&quot;&gt;Content-Encoding&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Some limitations are built into the &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; format:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; files &lt;strong&gt;must&lt;/strong&gt; be square.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; files &lt;strong&gt;must not&lt;/strong&gt; exceed &lt;code class=&quot;highlighter-rouge&quot;&gt;256px&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;something-for-nothing&quot;&gt;Something for Nothing&lt;/h2&gt;

&lt;p&gt;As with any asset being served, a web site’s favicon must be requested by a browser before a response (the file) is returned; this almost always requires markup to be added to the page. A favicon may be requested by adding a &lt;code class=&quot;highlighter-rouge&quot;&gt;link&lt;/code&gt; to the &lt;code class=&quot;highlighter-rouge&quot;&gt;head&lt;/code&gt; of the page:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-html&quot; data-lang=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;link&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;rel=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;icon&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;image/x-icon&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;href=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;/favicon.ico&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It is possible, however, to serve a favicon to a browser &lt;em&gt;without any markup at all&lt;/em&gt;. Browsers will automatically make a request to &lt;code class=&quot;highlighter-rouge&quot;&gt;favicon.ico&lt;/code&gt; at the root domain (e.g. &lt;a href=&quot;/favicon.ico&quot;&gt;joeyhoer.com/favicon.ico&lt;/a&gt;); if an &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; encoded file is served at that location, it will be used by default. This behavior may be overridden by defining a &lt;code class=&quot;highlighter-rouge&quot;&gt;link&lt;/code&gt; in the document &lt;code class=&quot;highlighter-rouge&quot;&gt;head&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;creating-favicons&quot;&gt;Creating Favicons&lt;/h2&gt;

&lt;p&gt;To convert images to &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; format, authors may use ImageMagick.&lt;/p&gt;

&lt;p&gt;Because &lt;code class=&quot;highlighter-rouge&quot;&gt;SVG&lt;/code&gt; favicons are not yet supported, it is recommended that individual icons are drawn specifically for each output size. This offers designers fine control over how the icon is displayed. The following command will generate an &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; file from two (or more) input &lt;code class=&quot;highlighter-rouge&quot;&gt;PNG&lt;/code&gt; files.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;convert &lt;span class=&quot;s1&quot;&gt;'source-16.png'&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'source-32.png'&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'favicon.ico'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Nevertheless, separate files may not always be available. Fortunately, ImageMagic supports many different image manipulation operations that can be used to perfect resizing. At a basic level, the following command may be used to automatically generate a favicon:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Trim and resize image&lt;/span&gt;
convert &lt;span class=&quot;s1&quot;&gt;'source.png'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-trim&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-resize&lt;/span&gt; 256x256 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-gravity&lt;/span&gt; center &lt;span class=&quot;nt&quot;&gt;-background&lt;/span&gt; transparent &lt;span class=&quot;nt&quot;&gt;-extent&lt;/span&gt; 256x256 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-define&lt;/span&gt; icon:auto-resize&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;16,32 &lt;span class=&quot;nt&quot;&gt;-compress&lt;/span&gt; zip &lt;span class=&quot;s1&quot;&gt;'favicon.ico'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, the command below offers additional control over how each size is generated. As is, the command is essentially equal to the &lt;code class=&quot;highlighter-rouge&quot;&gt;auto-resize&lt;/code&gt; command above.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;convert &lt;span class=&quot;s1&quot;&gt;'source.png'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-trim&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-resize&lt;/span&gt; 256x256 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-gravity&lt;/span&gt; center &lt;span class=&quot;nt&quot;&gt;-background&lt;/span&gt; transparent &lt;span class=&quot;nt&quot;&gt;-extent&lt;/span&gt; 256x256 &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-clone&lt;/span&gt; 0 &lt;span class=&quot;nt&quot;&gt;-resize&lt;/span&gt; 16x16 &lt;span class=&quot;se&quot;&gt;\)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;se&quot;&gt;\(&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-clone&lt;/span&gt; 0 &lt;span class=&quot;nt&quot;&gt;-resize&lt;/span&gt; 32x32 &lt;span class=&quot;se&quot;&gt;\)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-delete&lt;/span&gt; 0 &lt;span class=&quot;nt&quot;&gt;-compress&lt;/span&gt; zip &lt;span class=&quot;s1&quot;&gt;'favicon.ico'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The &lt;code class=&quot;highlighter-rouge&quot;&gt;-compress&lt;/code&gt; option in the code snippets above is set to &lt;code class=&quot;highlighter-rouge&quot;&gt;zip&lt;/code&gt;.
As &lt;a href=&quot;http://www.imagemagick.org/script/command-line-options.php#compress&quot;&gt;ImageMagick’s documentation on the &lt;code class=&quot;highlighter-rouge&quot;&gt;-compress&lt;/code&gt; option&lt;/a&gt; states:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When writing an &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; file, you may request that the images be encoded in &lt;code class=&quot;highlighter-rouge&quot;&gt;PNG&lt;/code&gt;
format, by specifying &lt;code class=&quot;highlighter-rouge&quot;&gt;Zip&lt;/code&gt; compression.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, zip compression only appears to work when converting images to &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt; files of &lt;code class=&quot;highlighter-rouge&quot;&gt;256px&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;

&lt;h1 id=&quot;extracting-images-from-favicons&quot;&gt;Extracting images from Favicons&lt;/h1&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# This will print the size, file type, calculated image type, calculated boolean for full opacity&lt;/span&gt;
identify &lt;span class=&quot;nt&quot;&gt;-format&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%wx%h %m %[type] %[opaque]&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;optimization&quot;&gt;Optimization&lt;/h2&gt;

&lt;h3 id=&quot;quantization&quot;&gt;Quantization&lt;/h3&gt;

&lt;p&gt;Quantization, or reducing the overall colors in an image, allows favicons to be optimized. The following function will determine the total number of unique colors in an image, and quantize to that number:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;quantize&lt;span class=&quot;o&quot;&gt;(){&lt;/span&gt;
  mogrify &lt;span class=&quot;nt&quot;&gt;-colors&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;identify &lt;span class=&quot;nt&quot;&gt;-format&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'%k'&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; +remap &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
quantize &lt;span class=&quot;s1&quot;&gt;'favicon.ico'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;icon-tools&quot;&gt;Icon Tools&lt;/h2&gt;

&lt;p&gt;Listed below are a number of tools that can be useful when producing icons for the web.&lt;/p&gt;

&lt;dl class=&quot;compact ul&quot;&gt;
  &lt;dt&gt;&lt;a href=&quot;http://www.nongnu.org/icoutils/&quot;&gt;icoutils&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;a set of command-line programs for extracting and converting images in Microsoft Windows® icon and cursor files. The &lt;code class=&quot;highlighter-rouge&quot;&gt;icotool&lt;/code&gt; program converts icon and cursor files into a set of PNG images.&lt;/dd&gt;
  &lt;dt&gt;&lt;a href=&quot;https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man1/iconutil.1.html&quot;&gt;iconutil&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;a utility to convert between &lt;code class=&quot;highlighter-rouge&quot;&gt;.iconset&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;.icns&lt;/code&gt; files.&lt;/dd&gt;
  &lt;dt&gt;&lt;a href=&quot;http://icns.sourceforge.net&quot;&gt;libicns&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;a library for manipulation of the Mac OS &lt;code class=&quot;highlighter-rouge&quot;&gt;icns&lt;/code&gt; resource format&lt;/dd&gt;
  &lt;dt&gt;&lt;a href=&quot;http://www.winterdrache.de/freeware/png2ico/&quot;&gt;png2ico&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;converts a &lt;code class=&quot;highlighter-rouge&quot;&gt;PNG&lt;/code&gt; to an &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt;&lt;/dd&gt;
  &lt;dt&gt;&lt;a href=&quot;http://www.telegraphics.com.au/sw/product/ICOFormat&quot;&gt;ICOFormat&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;a Photoshop plugin that allows &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt;s to be exported directly from Photoshop&lt;/dd&gt;
  &lt;dt&gt;&lt;a href=&quot;http://www.telegraphics.com.au/sw/product/ICOBundle&quot;&gt;ICOBundle&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;a Photoshop plugin that allows &lt;code class=&quot;highlighter-rouge&quot;&gt;ICO&lt;/code&gt;s to be exported directly from Photoshop&lt;/dd&gt;
&lt;/dl&gt;

&lt;h2 id=&quot;clearing-favicon-cache&quot;&gt;Clearing favicon cache&lt;/h2&gt;

&lt;p&gt;When developing and testing favicons, it is useful to understand how to clear the favicon cache.&lt;/p&gt;

&lt;h3 id=&quot;clear-safari-favicon-cache&quot;&gt;Clear Safari favicon cache&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Remove all favicons from Safari&lt;/span&gt;
sqlite3 ~/Library/Safari/WebpageIcons.db &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;delete from IconData; delete from PageURL; delete from IconInfo;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Delete the favicon of a single domain from Safari&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# delete from IconInfo where&lt;/span&gt;
sqlite3 ~/Library/Safari/WebpageIcons.db &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;update IconInfo set stamp = 0 where url like '%url.com%';&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;clear-chrome-favicon-cache&quot;&gt;Clear Chrome favicon cache&lt;/h3&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Remove all favicons from Chrome&lt;/span&gt;
sqlite3 ~/Library/Application&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;Support/Google/Chrome/Default/Favicons &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;delete from favicons;&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Delete the favicon of a single domain from Chrome&lt;/span&gt;
sqlite3 ~/Library/Application&lt;span class=&quot;se&quot;&gt;\ &lt;/span&gt;Support/Google/Chrome/Default/Favicons &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&quot;delete * from favicons where url like '%url.com%';&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Ambient Video</title>
   <link href="https://joeyhoer.com/ambient-video-45f150cd"/>
   <updated>2016-08-06T09:15:47-04:00</updated>
   <id>https://joeyhoer.com/ambient-video</id>
   <content type="html">&lt;p&gt;Ambient videos create engaging, “interactive” layouts&lt;sup id=&quot;fnref:1&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; by setting a video as the background of a page or a section of a page. While this design technique can evoke a powerful emotional response, special care must be taken to ensure that implementation does not negatively impact user experience.&lt;/p&gt;

&lt;p&gt;As a rule, ambient videos (also referred to as “environmental videos” or background videos) should not include audio. Generally, these videos do not require user interaction to begin playback and loop seamlessly. In many ways, ambient videos resemble animated GIFs; cinemagraphs are particularly well suited for ambient videos.&lt;/p&gt;

&lt;p&gt;Challenges of ambient video include accessibility, playback compatibility, and performance.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;demo&quot;&gt;Demo&lt;/h2&gt;

&lt;div class=&quot;ambient-video meme&quot;&gt;
  &lt;div class=&quot;content&quot;&gt;
    Ambient Video
  &lt;/div&gt;
  &lt;video autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot; poster=&quot;/assets/9th-street-poster-b333a6d6d448ce77a60b1c0fbe5f46ebc2df76c254ba3e3b62d92b302660d7ac.jpg&quot;&gt;
    &lt;source src=&quot;/assets/9th-street-d4f75e949f8e8b05d675c644785603ddd3af1415bf42dee2219b8fcca8c2c147.mp4&quot; type=&quot;video/mp4&quot; /&gt;
  &lt;/video&gt;
&lt;/div&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-html&quot; data-lang=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ambient-video&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;overlay&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    Overlay Content
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;video&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;autoplay&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;loop&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;muted&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;playsinline&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;poster=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ambient-video.jpg&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;source&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;ambient-video.mp4&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;video/mp4&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;/video&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-scss highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-scss&quot; data-lang=&quot;scss&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Hide play button from inline autoplay videos on iOS
&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;video&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;autoplay&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;playsinline&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;err&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;webkit-media-controls-play-button&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;&amp;amp;::-&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;webkit-media-controls-start-playback-button&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;opacity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;pointer-events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;accessibility&quot;&gt;Accessibility&lt;/h2&gt;

&lt;p&gt;Ambient videos frequently appear behind text, buttons and other elements. Ensure that all text and calls to action are clearly readable and distinguishable from the background content.&lt;/p&gt;

&lt;p&gt;One way to improve contrast between foreground and background elements is to darken the background by applying a filter to the video. This can be done on the video asset directly (which may improve compression levels, making the file smaller and improving performance) or with &lt;code class=&quot;highlighter-rouge&quot;&gt;CSS&lt;/code&gt;. It is also possible to add stokes, borders, and shadows to foreground elements to visually separate them from the background.&lt;/p&gt;

&lt;p&gt;Increasing the size of foreground elements can also improve accessibility. This may particularly be useful if the background includes rapid motion.&lt;/p&gt;

&lt;h2 id=&quot;playback-compatibility&quot;&gt;Playback Compatibility&lt;/h2&gt;

&lt;p&gt;Perhaps the most difficult challenge faced when implementing ambient video is mobile compatibility. Some mobile browsers disable preloading&lt;sup id=&quot;fnref:2&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;, automatic video playback&lt;sup id=&quot;fnref:3&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; and inline playback&lt;sup id=&quot;fnref:4&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. However, &lt;a href=&quot;https://webkit.org/blog/6784/new-video-policies-for-ios/&quot;&gt;new changes in iOS 10&lt;/a&gt; have returned “more control over media playback to web developers” for silent &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;video&amp;gt;&lt;/code&gt; elements – ideal for ambient videos.&lt;/p&gt;

&lt;p&gt;Until the features mentioned above are more widely supported, developers must circumvent the restrictions placed on mobile devices. Using JavaScript, developers may either &lt;a href=&quot;https://github.com/bfred-it/iphone-inline-video&quot;&gt;seek a video to simulate inline playback&lt;/a&gt;, or &lt;a href=&quot;https://github.com/Stanko/html-canvas-video-player&quot;&gt;load each frame&lt;/a&gt; &lt;a href=&quot;https://github.com/gka/canvid&quot;&gt;onto a &lt;code class=&quot;highlighter-rouge&quot;&gt;canvas&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Another option is to convert the video to an optimized &lt;code class=&quot;highlighter-rouge&quot;&gt;GIF&lt;/code&gt;. However, this method results in extremely poor quality “videos” that are very large in file size. For this reason, I strongly recommend against using the &lt;code class=&quot;highlighter-rouge&quot;&gt;GIF&lt;/code&gt; format for ambient videos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update (2017/12/06):&lt;/strong&gt; &lt;a href=&quot;https://webkit.org/blog/8016/release-notes-for-safari-technology-preview-43/&quot;&gt;Safari Tech Preview 43&lt;/a&gt; resolved an issue with &lt;a href=&quot;https://bugs.webkit.org/show_bug.cgi?id=176825&quot;&gt;“movie-backed” &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags&lt;/a&gt;, allowing short, looped, muted &lt;a href=&quot;https://calendar.perfplanet.com/2017/animated-gif-without-the-gif/&quot;&gt;MP4 files may now be loaded via &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;performance&quot;&gt;Performance&lt;/h2&gt;

&lt;p&gt;Video assets, particularly assets of sufficient quality to cover the viewport of large desktop browsers, are quite large in terms of file size.&lt;/p&gt;

&lt;p&gt;To ensure that site visitors are not waiting for a large asset to download, generate a “poster image” for the video, which will display while the video is loading. For a seamless experience, it is best to use the first frame of the video for the poster image.&lt;/p&gt;

&lt;p&gt;It is possible to easily generate a poster image from the first frame of a video using &lt;code class=&quot;highlighter-rouge&quot;&gt;ffmpeg&lt;/code&gt;. In its simplest form:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;ffmpeg &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$infile&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-vframes&lt;/span&gt; 1 &lt;span class=&quot;nt&quot;&gt;-ss&lt;/span&gt; 0 &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; image2 &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$outfile&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve written &lt;a href=&quot;https://github.com/joeyhoer/video-poster&quot;&gt;a script that allows your to quickly create poster images&lt;/a&gt; with &lt;code class=&quot;highlighter-rouge&quot;&gt;ffmpeg&lt;/code&gt;, and will allow you to optionally scale and blur the poster image for additional performance improvements.&lt;/p&gt;

&lt;p&gt;This can be used in combination with &lt;a href=&quot;https://github.com/joeyhoer/gifv&quot;&gt;gifv&lt;/a&gt; – another wrapper I’ve written around &lt;code class=&quot;highlighter-rouge&quot;&gt;ffmpeg&lt;/code&gt;, designed to generate high quality, low file size, ambient videos – built for the web.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot;&gt;

      &lt;p&gt;I say “interactive” because ambient videos present the perception of interactivity without actually providing any interactive functionality. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot;&gt;

      &lt;blockquote&gt;
        &lt;p&gt;In Safari on iOS (for all devices, including iPad), where the user may be on a cellular network and be charged per data unit, preload and autoplay are disabled. No data is loaded until the user initiates it.&lt;/p&gt;

        &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html#//apple_ref/doc/uid/TP40009523-CH5-SW4&quot;&gt;Safari Device-Specific Considerations Documentation&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
      &lt;/blockquote&gt;
      &lt;p&gt;&lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot;&gt;

      &lt;blockquote&gt;
        &lt;p&gt;To prevent unsolicited downloads over cellular networks at the user’s expense, embedded media cannot be played automatically in Safari on iOS—the user always initiates playback&lt;/p&gt;

        &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/AudioandVideoTagBasics/AudioandVideoTagBasics.html#//apple_ref/doc/uid/TP40009523-CH2-DontLinkElementID_7&quot;&gt;Safari Audio and Video Tag Basics Documentation &lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
      &lt;/blockquote&gt;
      &lt;p&gt;&lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot;&gt;

      &lt;blockquote&gt;
        &lt;p&gt;Safari optimizes video presentation for the smaller screen on iPhone or iPod touch by playing video using the full screen—video controls appear when the screen is touched, and the video is scaled to fit the screen in portrait or landscape mode. Video is not presented within the webpage.&lt;/p&gt;

        &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html#//apple_ref/doc/uid/TP40009523-CH5-SW2&quot;&gt;Safari Device-Specific Considerations Documentation&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
      &lt;/blockquote&gt;
      &lt;p&gt;&lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>Screen Recording</title>
   <link href="https://joeyhoer.com/screen-recording-0aa68518"/>
   <updated>2016-04-24T11:59:06-04:00</updated>
   <id>https://joeyhoer.com/screen-recording</id>
   <content type="html">&lt;p&gt;Creating screen recordings on macOS doesn’t have to be difficult, time consuming or expensive. You may be surprised to learn that pre-installed applications and tools in macOS can be used to quickly create high quality screen recordings and animated screenshots of your Mac, iPhone and iPad.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;QuickTime Player, which ships with macOS out of the box, includes built-in &lt;a href=&quot;https://support.apple.com/en-us/HT201066#record&quot;&gt;screen recording functionality&lt;/a&gt;. If you’re looking to create and share a short screen capture that does not require advanced video editing, QuickTime is an ideal solution.&lt;/p&gt;

&lt;p&gt;To create a new screen recording, open QuickTime Player, and select &lt;em&gt;File &amp;gt; New Screen Recording&lt;/em&gt; or use the
&lt;kbd class=&quot;combo&quot;&gt;
  &lt;kbd&gt;&lt;span&gt;&lt;abbr title=&quot;Cmd&quot;&gt;⌘&lt;/abbr&gt;&lt;/span&gt;&lt;/kbd&gt;
  &lt;kbd&gt;&lt;span&gt;&lt;abbr title=&quot;Option&quot;&gt;⌥&lt;/abbr&gt;&lt;/span&gt;&lt;/kbd&gt;
  &lt;kbd&gt;&lt;span&gt;N&lt;/span&gt;&lt;/kbd&gt;
&lt;/kbd&gt; keyboard shortcut.&lt;/p&gt;

&lt;p&gt;If you frequently need to create screen recordings, the following shell function may be used to quickly initiate a new recording:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Activate new QuickTime screen recording&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Note: Assistive access must be enabled for &quot;automated start&quot; to work&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;##&lt;/span&gt;
rec&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;# Enable Do Not Disturb&lt;/span&gt;
  dnd on

  &lt;span class=&quot;c&quot;&gt;# Activate new screen recording&lt;/span&gt;
  osascript &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ASCRIPT&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;' &amp;gt;/dev/null
    tell application &quot;QuickTime Player&quot;
      activate new screen recording
      -- Hit Spacebar
      tell application &quot;System Events&quot; to key code 49
      -- Alternative
      -- to tell window &quot;Screen Recording&quot; to click button 1
    end tell
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ASCRIPT

&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;# Click-drag to match bounds of application window&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$process&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read &lt;/span&gt;x y dx dy &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;osascript &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ASCRIPT&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
      -- Bring target application to front
      tell application &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot; to activate
      -- Bring QuickTime to front
      tell application &quot;QuickTime Player&quot; to activate
      -- Get window bounds
      set AppleScript's text item delimiters to &quot; &quot;
      tell application &quot;System Events&quot; to tell process &quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;&quot;
        set window_bounds to {position, size} of first window
        get window_bounds as text
      end tell
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;ASCRIPT
&lt;/span&gt;    &lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;

    clickdrag &lt;span class=&quot;nt&quot;&gt;-x&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$x&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$y&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-dx&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dx&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-dy&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$dy&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This script uses two options scripts &lt;a href=&quot;https://github.com/joeyhoer/dnd&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;dnd&lt;/code&gt;&lt;/a&gt; (which controls macOS’s Do Not Disturb mode) and &lt;a href=&quot;https://github.com/joeyhoer/clickdrag&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;clickdrag&lt;/code&gt;&lt;/a&gt; (which controls the mouse, and allows the script to automatically record a single application window). You may install these scripts with the following command:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;brew tap joeyhoer/homebrew-extras
brew install dnd &lt;span class=&quot;nt&quot;&gt;--HEAD&lt;/span&gt;
brew install clickdrag &lt;span class=&quot;nt&quot;&gt;--HEAD&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Tip:&lt;/strong&gt; An active screen recording may be stopped with the &lt;kbd class=&quot;combo&quot;&gt;
  &lt;kbd&gt;&lt;span&gt;&lt;abbr title=&quot;Cmd&quot;&gt;⌘&lt;/abbr&gt;&lt;/span&gt;&lt;/kbd&gt;
  &lt;kbd&gt;&lt;span&gt;&lt;abbr title=&quot;Ctrl&quot;&gt;⌃&lt;/abbr&gt;&lt;/span&gt;&lt;/kbd&gt;
  &lt;kbd&gt;&lt;span&gt;&lt;abbr title=&quot;Esc&quot;&gt;⎋&lt;/abbr&gt;&lt;/span&gt;&lt;/kbd&gt;
&lt;/kbd&gt; keyboard shortcut.&lt;/p&gt;

&lt;p&gt;The QuickTime screen recorder has options that can be set both through the GUI and on the command line.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Set recording quality&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# High:    MGCompressionPresetHighQuality&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Maximum: MGCompressionPresetMaximumQuality&lt;/span&gt;
defaults write com.apple.QuickTimePlayerX MGRecordingCompressionPresetIdentifier &lt;span class=&quot;nt&quot;&gt;-string&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'MGCompressionPresetMaximumQuality'&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Show mouse clicks in screen recordings&lt;/span&gt;
defaults write com.apple.QuickTimePlayerX MGScreenRecordingDocumentShowMouseClicksUserDefaultsKey &lt;span class=&quot;nt&quot;&gt;-bool&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;QuickTime provides some &lt;a href=&quot;https://support.apple.com/en-us/HT201066#edit&quot;&gt;very basic, easy-to-use video editing functions&lt;/a&gt; like trimming and clipping. Unfortunately, more advanced functions like changing the video dimensions and speed are not available, and QuickTime can only export videos in predefined formats (which are not optimized for sharing).&lt;/p&gt;

&lt;h2 id=&quot;optimizing-with-automator&quot;&gt;Optimizing with Automator&lt;/h2&gt;

&lt;p&gt;It is possible, however, to automatically optimize screen recordings for sharing by using an Automator workflow. Automator, another application that ships with macOS by default, is a very powerful tool that allows you to easily automate actions on your machine.&lt;/p&gt;

&lt;p&gt;To run a script automatically whenever a new file is added to a particular folder, use Automator to create a new &lt;em&gt;Folder Action&lt;/em&gt;. Once you’ve opened a new &lt;em&gt;Folder Action&lt;/em&gt;, select a folder where you will save your video files to (I use &lt;code class=&quot;highlighter-rouge&quot;&gt;~/Movies/&lt;/code&gt;). In the &lt;em&gt;Library&lt;/em&gt; panel locate the &lt;em&gt;Filter Finder Items&lt;/em&gt; action (found within the &lt;em&gt;Files &amp;amp; Folders&lt;/em&gt; actions), and drag-and-drop the action into the workflow. You now have the ability to filter out files that you do not wish to optimize. I filter by files created today, of type movie, with a &lt;code class=&quot;highlighter-rouge&quot;&gt;mov&lt;/code&gt; file extension. Next, locate the &lt;em&gt;Run Shell Script&lt;/em&gt; action (found within the &lt;em&gt;Utilities&lt;/em&gt; actions), ensure input is passed &lt;em&gt;as arguments&lt;/em&gt;, and in the body of the action, call the automated script you wish to run on the files.&lt;/p&gt;

&lt;p&gt;The following script uses &lt;a href=&quot;https://github.com/joeyhoer/gifv&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gifv&lt;/code&gt;&lt;/a&gt; to create an optimized video file that is half the resolution and 2.5× faster than the original video. This can significantly reduce file size, and make the video file easier to share.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/.exports
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PATH&lt;/span&gt;:/usr/local/bin:/usr/local/var/rbenv/shims

&lt;span class=&quot;c&quot;&gt;# Optimize videos at double speed and half resolution&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Minimum resolution&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;min_res&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;480
gifv &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;trunc(max(min(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$min_res&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\,&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;iw)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\,&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;iw/2)/2)*2:-2&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; 2.5 &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;gifv&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gifv&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gifv&lt;/code&gt; is a command-line wrapper around &lt;code class=&quot;highlighter-rouge&quot;&gt;ffmpeg&lt;/code&gt; that converts video files and &lt;code class=&quot;highlighter-rouge&quot;&gt;GIF&lt;/code&gt;s into videos optimized for &lt;code class=&quot;highlighter-rouge&quot;&gt;GIF&lt;/code&gt;-like playback on the web.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;highlighter-rouge&quot;&gt;gifv&lt;/code&gt; tool was based on two “competing formats”, &lt;a href=&quot;http://imgur.com/blog/2014/10/09/introducing-gifv/&quot;&gt;imgur’s &lt;code class=&quot;highlighter-rouge&quot;&gt;GIFV&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;http://www.gfycat.com/about&quot;&gt;gfycat’s &lt;code class=&quot;highlighter-rouge&quot;&gt;GFY&lt;/code&gt;&lt;/a&gt; (both pronounced “jiffy”). Essentially, these “formats” are wrappers around compressed video files (h.264 encoded MP4s or WebMs).&lt;/p&gt;

&lt;p&gt;Whenever possible, video files should be preferred to &lt;code class=&quot;highlighter-rouge&quot;&gt;GIF&lt;/code&gt;s, as video codecs offer superior compression, which results in smaller filesizes, better quality, and includes built-in playback controls.&lt;/p&gt;

&lt;h2 id=&quot;installation&quot;&gt;Installation&lt;/h2&gt;

&lt;p&gt;Install &lt;code class=&quot;highlighter-rouge&quot;&gt;gifv&lt;/code&gt;, add the script above to your &lt;code class=&quot;highlighter-rouge&quot;&gt;~/bin/&lt;/code&gt; directory and install the Automator Folder Action workflow below by downloading it and moving it to &lt;code class=&quot;highlighter-rouge&quot;&gt;~/Library/Workflows/Applications/Folder Actions&lt;/code&gt;. Once you have the Folder Action in place, you must enable Folder Actions and assign the action to a folder. To do this, open the &lt;code class=&quot;highlighter-rouge&quot;&gt;Folder Actions Setup&lt;/code&gt; app, check the box next to “Enable Folder Actions”, add a folder you wish to apply the action to (I chose the user’s &lt;code class=&quot;highlighter-rouge&quot;&gt;Movies&lt;/code&gt; folder), and add the script as seen in the screenshot below.&lt;/p&gt;

&lt;div class=&quot;placeholder screenshot-window&quot; style=&quot;--aspect-ratio:calc(100%/(558/214))&quot;&gt;
  &lt;div style=&quot;max-width:558px&quot;&gt;&lt;/div&gt;
  &lt;img alt=&quot;Screenshot of Folder Actions Setup on macOS&quot; width=&quot;558&quot; height=&quot;214&quot; integrity=&quot;sha256-btH73R1O5CarmKbMsplfcQmMdWLmbcX/uXTJXX23AuY=&quot; crossorigin=&quot;anonymous&quot; src=&quot;/assets/folder-actions-setup-6ed1fbdd1d4ee426ab98a6ccb2995f71098c7562e66dc5ffb974c95d7db702e6.png&quot; /&gt;
&lt;/div&gt;

&lt;p&gt;All videos created by QuickTime, saved to &lt;code class=&quot;highlighter-rouge&quot;&gt;~/Movies/&lt;/code&gt; will be automatically optimized.&lt;/p&gt;

&lt;p class=&quot;downloads&quot;&gt;&lt;a href=&quot;/downloads/Optimize%20Screen%20Recordings.workflow.zip&quot; download=&quot;&quot;&gt;&lt;i&gt;&lt;/i&gt;&lt;span&gt;Optimize Screen Recording&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;other-tools&quot;&gt;Other Tools&lt;/h2&gt;

&lt;h3 id=&quot;gifify&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gifify&lt;/code&gt;&lt;/h3&gt;

&lt;p&gt;Some services do not allow video files to be used, but do accept animated &lt;code class=&quot;highlighter-rouge&quot;&gt;GIF&lt;/code&gt;s. In these cases, you can use &lt;a href=&quot;https://github.com/joeyhoer/gifify&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;gifify&lt;/code&gt;&lt;/a&gt; to convert the video file to an animated &lt;code class=&quot;highlighter-rouge&quot;&gt;GIF&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The version of &lt;code class=&quot;highlighter-rouge&quot;&gt;gifify&lt;/code&gt; linked above if a fork of &lt;a href=&quot;https://github.com/jclem/gifify&quot;&gt;Jonathan Clem’s &lt;code class=&quot;highlighter-rouge&quot;&gt;gifify&lt;/code&gt;&lt;/a&gt; which provides some additional options such as reversed and parametric (or patrol) playback, as well as a number of different options for quality and compression/generation speed.&lt;/p&gt;

&lt;p&gt;Another option is to use the &lt;a href=&quot;https://github.com/vvo/gifify&quot;&gt;node package of the same name&lt;/a&gt;, which offers even more options.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Fluid Media for Responsive Designs</title>
   <link href="https://joeyhoer.com/fluid-media-for-responsive-designs-ce016587"/>
   <updated>2016-02-26T22:59:59-05:00</updated>
   <id>https://joeyhoer.com/fluid-media-for-responsive-designs</id>
   <content type="html">&lt;p&gt;Responsive images and other fluid media (like videos) are fundamental elements of modern web development. Despite the fact that responsive design has become ubiquitous in the web industry, a solution for high quality media that loads quickly and looks great on all viewports remains elusive.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;There are two factors which determine how media is presented in the browser:&lt;/p&gt;

&lt;dl class=&quot;compact ol&quot;&gt;
  &lt;dt&gt;Applied Styles&lt;/dt&gt;
  &lt;dd&gt;the &lt;em&gt;styling&lt;/em&gt; which is applied to a media element which defines it’s appearance (dimensions) at different viewport sizes.&lt;/dd&gt;
  &lt;dt&gt;Intrinsic Dimensions&lt;/dt&gt;
  &lt;dd&gt;the &lt;em&gt;inherent&lt;/em&gt; dimensions encoded within the metadata of a media element.&lt;/dd&gt;
&lt;/dl&gt;

&lt;h2 id=&quot;fluid-layout&quot;&gt;Fluid Layout&lt;/h2&gt;

&lt;p class=&quot;codepen&quot; data-embed-version=&quot;2&quot; data-theme-id=&quot;dark&quot; data-rerun-position=&quot;hidden&quot; data-slug-hash=&quot;BWEjjQ&quot; data-height=&quot;400&quot;&gt;
  See the Pen &lt;a href=&quot;//codepen.io/pen/BWEjjQ&quot;&gt;BWEjjQ&lt;/a&gt;
  by Joey Hoer (&lt;a href=&quot;//codepen.io/joeyhoer&quot;&gt;joeyhoer&lt;/a&gt;).
&lt;/p&gt;

&lt;h3 id=&quot;layout-attributes&quot;&gt;Layout Attributes&lt;/h3&gt;

&lt;p&gt;When thinking about how a media element’s dimensions affects page layout, it is important to consider how dimensions are calculated by the browser. Each image has an &lt;strong&gt;&lt;a href=&quot;https://www.w3.org/TR/css3-images/#intrinsic-dimensions&quot;&gt;intrinsic width and height&lt;/a&gt;&lt;/strong&gt; which is defined in the image metadata or described in the encoded image data &lt;em&gt;and&lt;/em&gt; a &lt;strong&gt;layout width and height&lt;/strong&gt; which is defined by the size at which the image is actually rendered on the page.&lt;/p&gt;

&lt;p&gt;Before the browser can layout the appropriate amount of space on the page based on the intrinsic dimensions, it must download the image (or a sufficient amount of metadata from the image). This can lead to content “jumping” when a &lt;em&gt;reflow&lt;/em&gt; occurs as the image dimensions are discovered by the browser during download. Fortunately, the HTML specification provides &lt;code class=&quot;highlighter-rouge&quot;&gt;width&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;height&lt;/code&gt; attributes on image elements to allow authors to define the desired size of the image. I will refer to these attributes as &lt;strong&gt;“layout attributes”&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The height and width attributes give user agents an idea of the size of an
image or object so that they may reserve space for it and continue rendering
the document while waiting for the image data.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;http://www.w3.org/TR/html401/struct/objects.html#visual&quot;&gt;HTML 4.01 specification&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;old-standards&quot;&gt;Old Standards&lt;/h4&gt;

&lt;p&gt;It was once best practice to define both the &lt;code class=&quot;highlighter-rouge&quot;&gt;width&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;height&lt;/code&gt; attributes of image elements to ensure that a page rendered quickly and allowed images to load asynchronously without triggering layout reflows and repaints. As older versions of Google’s Page Speed recommendations explained:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When the browser lays out the page, it needs to be able to flow around
replaceable elements such as images. It can begin to render a page even
before images are downloaded, provided that it knows the dimensions to wrap
non-replaceable elements around. If no dimensions are specified in the
containing document, or if the dimensions specified don’t match those of the
actual images, the browser will require a reflow and repaint once the images
are downloaded. To prevent reflows, specify the width and height of all
images, either in the HTML tag, or in CSS.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://web.archive.org/web/20120314005210/http://code.google.com/speed/page-speed/docs/rendering.html#SpecifyImageDimensions&quot;&gt;Google Page Speed&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Google Page Speed article linked above is from a cached version from March 2012. I was unable to find any equivalent recommendation in more recent versions of Google Page Speed.&lt;/p&gt;

&lt;p&gt;Matching the layout attributes to the intrinsic dimensions of an image will ensure that the appropriate amount of space is reserved for the image. It is also possible to &lt;strong&gt;proportionally adjust&lt;/strong&gt; these layout attributes (based on the intrinsic dimensions) without affecting the aspect ratio of the rendered image (which may be useful for displaying images on displays with high pixel density). For example, a &lt;code class=&quot;highlighter-rouge&quot;&gt;400px × 200px&lt;/code&gt; image could be scaled proportionally to a size of &lt;code class=&quot;highlighter-rouge&quot;&gt;200px × 100px&lt;/code&gt; on devices with &lt;code class=&quot;highlighter-rouge&quot;&gt;2x&lt;/code&gt; pixel density.&lt;/p&gt;

&lt;math xmlns=&quot;&amp;mathml;&quot;&gt;
  &lt;mrow&gt;
    &lt;mfrac&gt;
      &lt;mi&gt;400px&lt;/mi&gt;
      &lt;mi&gt;200px&lt;/mi&gt;
    &lt;/mfrac&gt;
    &lt;mo&gt;=&lt;/mo&gt;
    &lt;mfrac&gt;
      &lt;mi&gt;200px&lt;/mi&gt;
      &lt;mi&gt;100px&lt;/mi&gt;
    &lt;/mfrac&gt;
    &lt;mo&gt;=&lt;/mo&gt;
    &lt;mfrac&gt;
      &lt;mi&gt;2&lt;/mi&gt;
      &lt;mi&gt;1&lt;/mi&gt;
    &lt;/mfrac&gt;
  &lt;/mrow&gt;
&lt;/math&gt;

&lt;p&gt;Images also have an &lt;strong&gt;intrinsic aspect ratio&lt;/strong&gt; which is based on the intrinsic dimensions. &lt;em&gt;When only one dimension is defined&lt;/em&gt;, and when the &lt;a href=&quot;https://www.w3.org/TR/CSS2/visudet.html#the-width-property&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;width&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://www.w3.org/TR/CSS2/visudet.html#the-height-property&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;height&lt;/code&gt;&lt;/a&gt; properties are set to &lt;code class=&quot;highlighter-rouge&quot;&gt;auto&lt;/code&gt; (the default value), the browser uses the image’s intrinsic aspect ratio to scale images proportionally to different sizes. In other words, if only one layout attribute is defined (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;width&lt;/code&gt;), the image will be scaled proportionally to match that dimension. This characteristic of images allows CSS to be used to smoothly scale images between different sizes in responsive design.&lt;/p&gt;

&lt;h2 id=&quot;fluid-width-images&quot;&gt;Fluid Width Images&lt;/h2&gt;

&lt;p&gt;To achieve fluid media elements, an image’s layout attributes must be overwritten with CSS. By setting the &lt;code class=&quot;highlighter-rouge&quot;&gt;max-width&lt;/code&gt; of and image to &lt;code class=&quot;highlighter-rouge&quot;&gt;100%&lt;/code&gt;, developers can ensure that images will never be larger than their parent container. Defining a maximum width, and allowing the height to be calculated using the intrinsic aspect ratio, discussed earlier, effectively creates a fluid image solution.&lt;/p&gt;

&lt;p&gt;However, an issue arises when &lt;code class=&quot;highlighter-rouge&quot;&gt;max-width&lt;/code&gt; is set on an image that also has the &lt;code class=&quot;highlighter-rouge&quot;&gt;height&lt;/code&gt; attribute defined. Because the image does not correctly maintain it’s aspect ratio as the parent container becomes smaller than the image width; instead the image becomes “smushed”. To work around this issue, the &lt;code class=&quot;highlighter-rouge&quot;&gt;height&lt;/code&gt; property must always be set to &lt;code class=&quot;highlighter-rouge&quot;&gt;auto&lt;/code&gt;—this can be accomplished with CSS.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-css&quot; data-lang=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;auto&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;codepen&quot; data-embed-version=&quot;2&quot; data-theme-id=&quot;dark&quot; data-rerun-position=&quot;hidden&quot; data-slug-hash=&quot;RGObWK&quot; data-height=&quot;520&quot;&gt;
  See the Pen &lt;a href=&quot;//codepen.io/pen/RGObWK&quot;&gt;Visualizing Fluid Media&lt;/a&gt;
  by Joey Hoer (&lt;a href=&quot;//codepen.io/joeyhoer&quot;&gt;joeyhoer&lt;/a&gt;).
&lt;/p&gt;

&lt;div class=&quot;note&quot;&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Some developers may be confused here, knowing that inline styles have a higher specificity than other CSS rules. While this is true, “presentational attributes” (e.g. &lt;code class=&quot;highlighter-rouge&quot;&gt;width&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;height&lt;/code&gt; attributes) &lt;em&gt;are not the same as&lt;/em&gt; “inline styles” (i.e. the &lt;code class=&quot;highlighter-rouge&quot;&gt;style&lt;/code&gt; attribute). The CSS specification outlines how browsers may implement specificity for “stylistic attributes”:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The UA may choose to honor presentational attributes in an HTML source
document. If so, these attributes are translated to the corresponding CSS
rules with specificity equal to 0, and are treated as if they were inserted
at the start of the author style sheet. They may therefore be overridden by
subsequent style sheet rules. In a transition phase, this policy will make it
easier for stylistic attributes to coexist with style sheets.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;http://www.w3.org/TR/CSS21/cascade.html#preshint&quot;&gt;CSS 2.1 specification §6.4.4&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/div&gt;

&lt;h3 id=&quot;other-elements&quot;&gt;Other Elements&lt;/h3&gt;

&lt;p&gt;The same styling may also be applied to other media elements which have an intrinsic height, allowing them to maintain their aspect ratio as they scale (when height is set to &lt;code class=&quot;highlighter-rouge&quot;&gt;auto&lt;/code&gt;). These elements include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;audio&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;canvas&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;img&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;svg&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;video&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other elements may also benefit from having a &lt;code class=&quot;highlighter-rouge&quot;&gt;max-width&lt;/code&gt; of &lt;code class=&quot;highlighter-rouge&quot;&gt;100%&lt;/code&gt; applied, but do not have an intrinsic height. These elements include:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;select&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;textarea&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;input&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;iframe&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;embed&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;object&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;reflows-on-fluid-images&quot;&gt;Reflows on Fluid Images&lt;/h3&gt;

&lt;p&gt;A layout issue occurs before the image has loaded; if &lt;code class=&quot;highlighter-rouge&quot;&gt;height&lt;/code&gt; is set to &lt;code class=&quot;highlighter-rouge&quot;&gt;auto&lt;/code&gt; and the intrinsic dimensions have not yet been determined, the calculated image height will be &lt;code class=&quot;highlighter-rouge&quot;&gt;0&lt;/code&gt;. Because the most common fluid image technique (seen above) relies on the &lt;code class=&quot;highlighter-rouge&quot;&gt;height&lt;/code&gt; being set to &lt;code class=&quot;highlighter-rouge&quot;&gt;auto&lt;/code&gt;, this approach causes reflows and repaints as images are downloaded.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;An img element is initially unavailable.&lt;/p&gt;

  &lt;p&gt;When an img element is [partially or completely] available, it provides a paint source whose width is the image’s intrinsic width, whose height is the image’s intrinsic height, and whose appearance is the intrinsic appearance of the image.&lt;/p&gt;

  &lt;p&gt;[The] attributes width and height must return the rendered width and height of the image … if the image is being rendered … to a visual medium; or else the intrinsic width and height of the image … if the image is available but not being rendered to a visual medium; or else 0, if the image is not available.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://www.w3.org/TR/html5/embedded-content-0.html#the-img-element&quot;&gt;HTML5 Embedded Content Specification §4.7.5. The img element&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;placeholder-solutions&quot;&gt;Placeholder Solutions&lt;/h4&gt;

&lt;p&gt;One solution to prevent reflows when using fluid images is to utilize a placeholder element that reserves space for the image. This technique is common for lazy-loaded images, but provides benefits to all users, as devices with poor connections may require some time to download images, and content reflows always present a jarring user experience.&lt;/p&gt;

&lt;p&gt;For a placeholder solutions to be effective, the author must know the desired aspect ratio of the image in advance.&lt;/p&gt;

&lt;h5 id=&quot;canvas-placeholder&quot;&gt;Canvas Placeholder&lt;/h5&gt;

&lt;p&gt;For example, a blank &lt;code class=&quot;highlighter-rouge&quot;&gt;canvas&lt;/code&gt; element could be used:&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-html&quot; data-lang=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;placeholder&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ width }}&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{{ height }}&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;…&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;alt=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;…&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-scss highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-scss&quot; data-lang=&quot;scss&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.placeholder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline-block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;relative&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;canvas&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this example, a &lt;code class=&quot;highlighter-rouge&quot;&gt;canvas&lt;/code&gt; element is defined with &lt;code class=&quot;highlighter-rouge&quot;&gt;width&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;height&lt;/code&gt; attributes equal to that of the image which will be “replacing” it. The &lt;code class=&quot;highlighter-rouge&quot;&gt;canvas&lt;/code&gt; will be “painted over” when the image loads due to absolute positioning of the image. This technique works because the &lt;code class=&quot;highlighter-rouge&quot;&gt;canvas&lt;/code&gt; element scales proportionately when resized, even when constrained by a &lt;code class=&quot;highlighter-rouge&quot;&gt;max-width&lt;/code&gt;. This is because:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The intrinsic dimensions of the canvas element when it represents embedded content are equal to the dimensions of the element’s bitmap.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;&lt;a href=&quot;https://www.w3.org/TR/html5/scripting-1.html#the-canvas-element&quot;&gt;HTML5 Specification §4.11.4 The canvas element&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5 id=&quot;aspect-ratio-placeholder&quot;&gt;Aspect-Ratio Placeholder&lt;/h5&gt;

&lt;p&gt;Alternatively, a placeholder can be created with a &lt;code class=&quot;highlighter-rouge&quot;&gt;div&lt;/code&gt; rather than the &lt;code class=&quot;highlighter-rouge&quot;&gt;canvas&lt;/code&gt; element. By using a pseudo element to preserve the aspect ratio of the container, a fluid placeholder can be created, allowing the area to be “painted over” by absolutely positioning the image.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-html&quot; data-lang=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;class=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;placeholder&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;style=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;--aspect-ratio:calc(100%/({{ width }}/{{ height }}));max-width:{{ width }}px&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;…&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;alt=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;…&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-scss highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-scss&quot; data-lang=&quot;scss&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.placeholder&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inline-block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;relative&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;err&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;before&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;padding-top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;aspect-ratio&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;nt&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;sizes-attribute&quot;&gt;Sizes Attribute&lt;/h3&gt;

&lt;p&gt;In addition to the layout attributes (&lt;code class=&quot;highlighter-rouge&quot;&gt;width&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;height&lt;/code&gt;) described above, the &lt;code class=&quot;highlighter-rouge&quot;&gt;sizes&lt;/code&gt; attribute (introduced win HTML5) may also be used to specify varying images sizes for different pixel densities and viewport widths.&lt;/p&gt;

&lt;div class=&quot;language-html highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-html&quot; data-lang=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;…&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;width=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;height=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;sizes=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;100vw&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While these layout attributes are useful for preventing layout reflows, they do not directly make the images scale as their container scales.&lt;/p&gt;

&lt;h2 id=&quot;future-solutions&quot;&gt;Future Solutions&lt;/h2&gt;

&lt;p&gt;A proposal for a permanent solution to this problem is in development. The &lt;a href=&quot;https://github.com/WICG/intrinsicsize-attribute&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;intrinsicsize&lt;/code&gt; attribute&lt;/a&gt; may one day allow authors to define the aspect ratio of a media element in advance without the need for additional styling hooks or placeholder elements.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Responsive Aspect Ratio & Fluid Text</title>
   <link href="https://joeyhoer.com/responsive-aspect-ratio-fluid-text-c5fc28fe"/>
   <updated>2016-01-18T12:34:25-05:00</updated>
   <id>https://joeyhoer.com/responsive-aspect-ratio-fluid-text</id>
   <content type="html">&lt;p&gt;When designing, developing and optimizing a responsive website, you will often encounter elements which maintain their aspect ratio while scaling. Images do by default in HTML, but videos, background images, calls to action, ads, and other graphical elements do not.&lt;/p&gt;

&lt;p&gt;A developer’s first inclination may be to set the &lt;code class=&quot;highlighter-rouge&quot;&gt;width&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;height&lt;/code&gt; of an element to a percentage value. However, because percentages in CSS are calculated based on the dimensions of the equivalent property of the elements parent container (i.e. a percentage based &lt;code class=&quot;highlighter-rouge&quot;&gt;height&lt;/code&gt; will be based on the parent’s &lt;code class=&quot;highlighter-rouge&quot;&gt;height&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;width&lt;/code&gt; will be based on &lt;code class=&quot;highlighter-rouge&quot;&gt;width&lt;/code&gt;), decreasing an element’s width will not directly affect its height.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Some may advocate the use of JavaScript to calculate and set the &lt;code class=&quot;highlighter-rouge&quot;&gt;height&lt;/code&gt; of the element on &lt;code class=&quot;highlighter-rouge&quot;&gt;resize&lt;/code&gt;, while others might suggest using images set to &lt;code class=&quot;highlighter-rouge&quot;&gt;visibility: hidden&lt;/code&gt; or data URLs akin to a &lt;code class=&quot;highlighter-rouge&quot;&gt;spacer.gif&lt;/code&gt;. These solutions are inelegant and difficult to implement.&lt;/p&gt;

&lt;p&gt;There is, however, a simple elegant, pure &lt;code class=&quot;highlighter-rouge&quot;&gt;CSS&lt;/code&gt; solution to the problem:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-css&quot; data-lang=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;.aspect-ratio&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;relative&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;hidden&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.aspect-ratio&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;::after&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;''&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;padding-bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;33%&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;/* 1:3 Ratio - Change this value */&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;block&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.aspect-ratio&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;absolute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nc&quot;&gt;.aspect-ratio&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;.cover&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;bottom&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;nl&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;so-whats-the-trick&quot;&gt;So, what’s the trick?&lt;/h2&gt;

&lt;p&gt;The proof is in the &lt;code class=&quot;highlighter-rouge&quot;&gt;padding&lt;/code&gt;. According to &lt;a href=&quot;http://www.w3.org/TR/CSS2/box.html#propdef-padding&quot;&gt;the spec&lt;/a&gt;, when &lt;code class=&quot;highlighter-rouge&quot;&gt;padding&lt;/code&gt; is defined as a percentage it is calculated based on the “width of the containing block” (i.e. parent container).&lt;/p&gt;

&lt;p&gt;This may seem odd at first, but let’s examine the concept for a moment. If you define &lt;code class=&quot;highlighter-rouge&quot;&gt;padding: 10px&lt;/code&gt; for an element, you would correctly assume that the padding would be a uniform 10 pixels around the element. Similarly, if you assign &lt;code class=&quot;highlighter-rouge&quot;&gt;padding: 10%&lt;/code&gt; to an element, you would expect the padding to be uniform. If the top and bottom padding were calculated based on the height or the parent container rather than the width, the vertical &lt;code class=&quot;highlighter-rouge&quot;&gt;padding&lt;/code&gt; would only be equal to the horizontal &lt;code class=&quot;highlighter-rouge&quot;&gt;padding&lt;/code&gt; when the parent container was a perfect square.&lt;/p&gt;

&lt;p&gt;Because pseudo elements are children of the element on which they are declared, setting a pseudo element to &lt;code class=&quot;highlighter-rouge&quot;&gt;display: block&lt;/code&gt; will allow it to fill its parent horizontally and will allow it to expand vertically to the height of it’s contents. Assigning a percentage based &lt;code class=&quot;highlighter-rouge&quot;&gt;padding&lt;/code&gt; value to either property on the horizontal axis (&lt;code class=&quot;highlighter-rouge&quot;&gt;top&lt;/code&gt; or &lt;code class=&quot;highlighter-rouge&quot;&gt;bottom&lt;/code&gt;) will create a box which maintains an aspect ratio based on the parent’s width.&lt;/p&gt;

&lt;p&gt;Any content within the parent will also contribute to the height of the parent element. To prevent this, the direct descendants the containing block must be absolutely positioned. The &lt;code class=&quot;highlighter-rouge&quot;&gt;.cover&lt;/code&gt; class in the example code above, can be used to allow a direct descendant to fill the container. Other content can be positioned manually, as needed.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; Some content – like videos – will require the use of &lt;code class=&quot;highlighter-rouge&quot;&gt;width: 100%&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;height: 100%&lt;/code&gt; when covering an element.&lt;/p&gt;

&lt;h3 id=&quot;why-not-margins&quot;&gt;Why not margins?&lt;/h3&gt;

&lt;p&gt;If you read the spec you will know that margins are also calculated based on the width of the containing block, so why shouldn’t we use margins? &lt;strong&gt;Margins collapse, padding doesn’t.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;adding-images--content&quot;&gt;Adding Images &amp;amp; Content&lt;/h2&gt;

&lt;p&gt;Using this trick in combination with CSS viewport units (like &lt;code class=&quot;highlighter-rouge&quot;&gt;vw&lt;/code&gt;) and &lt;code class=&quot;highlighter-rouge&quot;&gt;background-size: cover&lt;/code&gt; allows developers to create completely fluid elements.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Attributes vs Categories</title>
   <link href="https://joeyhoer.com/attributes-vs-categories-6f3c9bab"/>
   <updated>2016-01-18T12:25:00-05:00</updated>
   <id>https://joeyhoer.com/attributes-vs-categories</id>
   <content type="html">&lt;p&gt;Information architecture is one of the most important user experience elements of a website because it directly affects navigation, often affects URL structure, and it is important to both SEO and conversion optimization. On ecommerce websites, product “merchandising” may impact aspects of information architecture. To ensure that users can find the information they are looking for as quickly and as easily as possible, it is important to understand the technical differences between categories and attributes.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;attributes&quot;&gt;Attributes&lt;/h2&gt;

&lt;p&gt;Attributes are a form of &lt;em&gt;description&lt;/em&gt; which outline the properties of an object (a product, a blog post, a user, etc.). An attribute defines an &lt;em&gt;objective&lt;/em&gt; feature or quality regarded as a characteristic or inherent part of an object. Generally attributes are &lt;em&gt;immutable&lt;/em&gt;, meaning they do not change over time. However, there are some attributes, such as a product’s rating or price, that may change over time. A single item may have a number of different attributes, but generally each item will have only one value for each attribute.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Attributes are generally &lt;em&gt;adjectives&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Not all attributes need to be visible to a user, however attributes that are displayed may be combined &lt;a href=&quot;https://schema.org&quot;&gt;with special markup&lt;/a&gt; and other technology to provide relevant information to search robots, crawlers, and assistive technology. This information can be used to enhance search results and provide a superior user experience. For example, attributes allow items to be easily compared, and because attributes may represent values on a range, attributes can be used to easily filter a large collection of items. This helps users quickly locate items that meet a specified criteria.&lt;/p&gt;

&lt;p&gt;A t-shirt has a &lt;em&gt;size&lt;/em&gt;, &lt;em&gt;color&lt;/em&gt;, and &lt;em&gt;style&lt;/em&gt; (and many other attributes like &lt;em&gt;price&lt;/em&gt;, &lt;em&gt;manufacturer&lt;/em&gt;, &lt;em&gt;material&lt;/em&gt;, and &lt;em&gt;rating&lt;/em&gt;). A consumer may want to compare &lt;em&gt;price&lt;/em&gt; and &lt;em&gt;rating&lt;/em&gt; for &lt;em&gt;black&lt;/em&gt;, &lt;em&gt;v-neck&lt;/em&gt; t-shirts that are either &lt;em&gt;small&lt;/em&gt; or &lt;em&gt;medium&lt;/em&gt;. Through the use of attributes, these types of complex filters and comparisons are made possible. The more accurately an object is described through attributes, the easier it will be to search, sort, or filter the object.&lt;/p&gt;

&lt;h2 id=&quot;categories&quot;&gt;Categories&lt;/h2&gt;

&lt;p&gt;Categories are a form of &lt;em&gt;classification&lt;/em&gt;, or grouping, according to shared qualities or characteristics. Categories (sometimes referred to as “collections”) are &lt;em&gt;subjective&lt;/em&gt; collections of items that may be curated manually or automatically based on a set of predefined rules. Categorization is subject to change over time and may be hierarchical, meaning that each category may have sub-classifications that allow a collection of items to be further refined. Objects may also exist simultaneously in any number of categories.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Categories are generally &lt;em&gt;nouns&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Categorization is heavily reliant upon marketing and merchandising and often defines a website’s URL structure and navigation, therefor it has a pronounced impact on both user experience and SEO.&lt;/p&gt;

&lt;h2 id=&quot;crossover&quot;&gt;Crossover&lt;/h2&gt;

&lt;p&gt;On ecommerce sites, it is common for an important attribute value to also be used as a category; this may happen if an attribute is particularly popular, necessary in navigation or SEO, or is essential to marketing. Generally when attributes must be surfaced as categories, it is best to automate the categorization with rules so that the overhead of catalog maintenance remains low.&lt;/p&gt;

&lt;p&gt;It may be helpful to think of categories as shelves, drawers or containers to place items inside of. Moving an item from one container to another does not change the qualities of the object itself (it’s attributes), but it does change it’s classification (category).&lt;/p&gt;

&lt;p&gt;Going back to our previous t-shirt example, a v-neck t-shirt may be included in a “Featured” category which highlights items that are highly valued by customers or business objectives; this type of category would not need to rely on an attribute because being “featured” is not an objective quality of the t-shirt itself, instead it is a subjective grouping. The same v-neck t-shirt may also appear in a “V-necks” category that is populated automatically with all the available v-neck t-shirts. This “promotion” of the &lt;em&gt;style&lt;/em&gt; attribute will allow visitors to locate the item, and other similar items more quickly particularly if the category is featured prominently in navigation, and by appearing in search results.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Flat vs Hierarchical URL Structure</title>
   <link href="https://joeyhoer.com/flat-vs-hierarchical-url-structure-420f178c"/>
   <updated>2016-01-18T11:02:20-05:00</updated>
   <id>https://joeyhoer.com/flat-vs-hierarchical-url-structure</id>
   <content type="html">&lt;p&gt;Some SEO consultants recommend “flat” URL structures. However, there is no evidence that a flat URL structure is better than a clearly hierarchical URL structure. While there is some debate in the SEO community, most reputable sources agree that there is no SEO/SERP benefit to flattening URL structure, and that hierarchical URLs are better for UX, crawling, and data analysis.&lt;/p&gt;

&lt;!--more--&gt;

&lt;blockquote&gt;
  &lt;p&gt;Since the flat site architecture concept appeared on the SEO horizon … many SEO consultants got it wrong. The flat site architecture concept is related to the click distance between pages in a site, and how relevancy is distributed according to internal links structure — yet has nothing to do with URLs.&lt;/p&gt;

  &lt;p&gt;The main misunderstanding was, and unfortunately still is, that you have to get rid of directories in URL structures. Although it is widely agreed that you may want to keep URLs short and locate keywords close to the root or left part of the URL, there are many reasons why you should keep a certain structure of folders or directories&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;http://www.bruceclay.com/blog/structured-urls/&quot;&gt;3 Reasons to Always Have Structured URLs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;what-is-a-flat-url&quot;&gt;What is a flat URL?&lt;/h2&gt;

&lt;p&gt;A flat URL refers to the path segment of a URL being not more than one “level” deep.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[The path contains data], usually organized in hierarchical form, that appears as a sequence of segments separated by slashes. Such a sequence may resemble or map exactly to a file system path, but does not always imply a relation to one.&lt;/p&gt;

  &lt;p&gt;– &lt;a href=&quot;https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#Syntax&quot;&gt;Uniform Resource Identifier Syntax, Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, a flat URL structure is one in which all URLs appear at the top level, with no hierarchy.&lt;/p&gt;

&lt;p&gt;Let’s imagine, for example, that you have a website selling greeting cards; you may have an assortment of cards which you categorize as “musical” (the kinds of cards that play back a recording when opened). You want users to be able to locate these cards independently, but you also want users to be able to navigate, via subcategories, to musical cards for specific occasions and holidays. Let’s examine how this would be treated in a hierarchical URL structure and a flat URL structure:&lt;/p&gt;

&lt;dl&gt;
  &lt;dt&gt;Hierarchical URLs&lt;/dt&gt;
  &lt;dd&gt;
    &lt;ul&gt;
      &lt;li&gt;example.com/holiday/new-year/musical/&lt;/li&gt;
      &lt;li&gt;example.com/holiday/musical/&lt;/li&gt;
      &lt;li&gt;example.com/occasion/birthday/musical/&lt;/li&gt;
      &lt;li&gt;example.com/occasion/musical/&lt;/li&gt;
      &lt;li&gt;example.com/musical/&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/dd&gt;
  &lt;dt&gt;Flat URLs&lt;/dt&gt;
  &lt;dd&gt;
    &lt;ul&gt;
      &lt;li&gt;example.com/holiday/&lt;/li&gt;
      &lt;li&gt;example.com/new-year/&lt;/li&gt;
      &lt;li&gt;example.com/occasion/&lt;/li&gt;
      &lt;li&gt;example.com/birthday/&lt;/li&gt;
      &lt;li&gt;example.com/musical/&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/dd&gt;
&lt;/dl&gt;

&lt;h2 id=&quot;how-flat-is-too-flat&quot;&gt;How flat is too flat?&lt;/h2&gt;

&lt;p&gt;As you may have noticed in the examples above, because there is no hierarchy in the flat URL structure, problems become apparent when multiple hierarchical categories use the same name (i.e. the delineation between “musical birthday cards” and “musical New Year’s cards”).&lt;/p&gt;

&lt;p&gt;While it may be technically possible to present different content at a single URL for each user based on their flow through the website or entry-point, this will create a very confusing user experience, and make it difficult or impossible for search engines to index the site’s content correctly, in it’s entirety.&lt;/p&gt;

&lt;p&gt;A solution to this problem is to maintain a pseudo hierarchical URL structure, replacing slashes (&lt;code class=&quot;highlighter-rouge&quot;&gt;/&lt;/code&gt;) with dashes (&lt;code class=&quot;highlighter-rouge&quot;&gt;-&lt;/code&gt;). For example:&lt;/p&gt;

&lt;dl&gt;
  &lt;dt&gt;Alternative Flat URLs&lt;/dt&gt;
  &lt;dd&gt;
    &lt;ul&gt;
      &lt;li&gt;example.com/holiday-new-year-musical/&lt;/li&gt;
      &lt;li&gt;example.com/holiday-musical/&lt;/li&gt;
      &lt;li&gt;example.com/occasion-birthday-musical/&lt;/li&gt;
      &lt;li&gt;example.com/occasion-musical/&lt;/li&gt;
      &lt;li&gt;example.com/musical/&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;However, the question remains: why use a flat URL structure over a hierarchical one?&lt;/p&gt;

&lt;h3 id=&quot;arguments-for-flat-urls&quot;&gt;Arguments for Flat URLs&lt;/h3&gt;

&lt;p&gt;There are four arguments SEO consultants make when recommending flat URLs over hierarchical URLs:&lt;/p&gt;

&lt;dl class=&quot;compact ol&quot;&gt;
  &lt;dt&gt;Nested PageRank&lt;/dt&gt;
  &lt;dd&gt;The further a page resides from the root domain, the less PageRank is attributed to that page.&lt;/dd&gt;
  &lt;dt&gt;URL keyword density&lt;/dt&gt;
  &lt;dd&gt;The longer the URL, the less emphasis is placed on targeted keywords in the URL.&lt;/dd&gt;
  &lt;dt&gt;Usability&lt;/dt&gt;
  &lt;dd&gt;The shorter a URL is, the more user-friendly, sharable, and marketable it is, leading to increased conversion.&lt;/dd&gt;
  &lt;dt&gt;URL migration&lt;/dt&gt;
  &lt;dd&gt;Relying on directories negatively affects the ability to migrate URLs in the future with minimal SEO impact.&lt;/dd&gt;
&lt;/dl&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; The arguments above make &lt;strong&gt;incorrect assumptions&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;url-structure-vs-site-architecture&quot;&gt;URL Structure vs Site Architecture&lt;/h2&gt;

&lt;p&gt;While it is true that long, unreadable URLs do not lend themselves to good SEO or good UX, a site’s depth (i.e. architecture) is measured by how many clicks are required to navigate from one page to another (i.e. “click distance”) and by how pages are cross-linked. URL structure, “directory” (i.e. path segment) count, and keyword positioning is irrelevant to a site’s “flatness” as interpreted by search engines.&lt;/p&gt;

&lt;p&gt;Google’s Matt Cutts debunks many of the incorrect assumptions about how URL structure affects search rankings:&lt;/p&gt;

&lt;dl&gt;
  &lt;dt&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=l_A1iRY6XTM&quot;&gt;Does the number of subdirectories in a URL affect its ranking?&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;blockquote&gt;
      &lt;p&gt;[At Google,] there is virtually no difference [between flat URLs and hierarchical URLs] … It can be very helpful for users if you have subdirectories … [Organizing content within subdirectories] can be a great thing for usability, and I definitely recommend having your site categorized like that … Having one or two extra directories [in your URLs is] not a major factor in Google’s search engine rankings.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/dd&gt;
  &lt;dt&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=UbimY0exQIA&quot;&gt;How does URL structure affect PageRank?&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;blockquote&gt;
      &lt;p&gt;Google doesn’t worry about how deep a set of directories is.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/dd&gt;
  &lt;dt&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=gRzMhlFZz9I&quot;&gt;Does the position of keywords in the URL affect ranking?&lt;/a&gt;&lt;/dt&gt;
  &lt;dd&gt;
    &lt;blockquote&gt;
      &lt;p&gt;[While] it does help a little bit to have keywords in the URL … if there is a convenient way [to structure the URLs in a way] that’s good for users … [don’t worry about] how deep the [keywords] are in the path; take the first 2–5 words [of the content] and use that as the URL. Don’t use [too many] words [in the URL], because that looks spammy to users, and people will probably not click through as much. [Worry less about the position of keywords in the URL, and focus more on] having great content that people want to link to or that people want to find out about.&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Google’s John Mueller also addressed URL length, stating the Google will &lt;a href=&quot;https://productforums.google.com/forum/#!category-topic/webmasters/crawling-indexing--ranking/h0aeO2_kpac&quot;&gt;“crawl and index URLs over 1000 characters long – but that doesn’t mean it’s a good practice”&lt;/a&gt;. The common SEO recommendation is to limit the full, indexed URL (including the scheme [&lt;code class=&quot;highlighter-rouge&quot;&gt;http&lt;/code&gt;/&lt;code class=&quot;highlighter-rouge&quot;&gt;https&lt;/code&gt;] and host [domain]) to between 75–115 characters in length to allow the full URL to be displayed in SERPs and preserve the readability and shareability of links.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Tip:&lt;/strong&gt; While shorter URLs are preferred for readability, excessively short URLs, and supremely flat URLs are not recommended. Users should be able to predict content based on the URL; shorter, readable, memorable URLs are good for users, and hierarchy can help in that.&lt;/p&gt;

&lt;h2 id=&quot;benefits-of-hierarchical-urls&quot;&gt;Benefits of Hierarchical URLs&lt;/h2&gt;

&lt;p&gt;Hierarchical URLs address the failings of flat URLs.&lt;/p&gt;

&lt;h3 id=&quot;search-engine-results-pages-serps&quot;&gt;Search Engine Results Pages (SERPs)&lt;/h3&gt;

&lt;p&gt;Structured hierarchical URLs reinforce a search engine’s semantic understanding of content; search engines use URLs to calculate link value, and highlight keywords in URLs on SERPs. URLs containing keywords hold more value, rank higher in results, and are more likely to draw a user’s attention.&lt;/p&gt;

&lt;p&gt;Because URLs appear in search engine results, hierarchical URLs with relevant keywords help localize a user in a site’s content, and provide hints at additional content that may be offered on a site – adding to a site’s authority.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Adding semantic markup and using schema (structured data) in combination with a well organized, hierarchical URL structure will increase the chances of &lt;a href=&quot;https://developers.google.com/search/docs/data-types/breadcrumbs&quot;&gt;breadcrumbs displaying on SERPs&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;user-experience&quot;&gt;User Experience&lt;/h3&gt;

&lt;p&gt;The primary argument for a flat URL structure is that flat URLs are memorable. Recent moves by search engines and browser vendors have shown that the memorability of URLs is not a critical element of a user journey. Ease of navigation and good SERP results are more important factors to discovery and conversion.&lt;/p&gt;

&lt;p&gt;“Intuitive URLs” are simple URLs that may be easily guessed by users, these types of URLs are easy to remember and can increase direct traffic. A hierarchical structure &lt;a href=&quot;http://www.stateofdigital.com/optimising-urls-seo-ux/&quot;&gt;provides a better user experience &amp;amp; improves the SERP&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The URL, as it appears in the browser toolbar, serves as a virtual breadcrumb trail. It provides users a general context to content hierarchy, and it provides an opportunity for more advanced users to manually alter the URL to retrace their path in to broader sections of the site. Removing this data removes relevant context from users.&lt;/p&gt;

&lt;h3 id=&quot;crawling&quot;&gt;Crawling&lt;/h3&gt;

&lt;p&gt;Hierarchical structures are a fundamental element of the web. Search engines and analytics tools are designed to crawl and analyze content and user flow through hierarchical tree structures. Structured URLs help semantics by revealing how a site’s content is structured.&lt;/p&gt;

&lt;p&gt;As mentioned above, flattening the URL structure removes important context from both users and search engines. In our example above, both &lt;code class=&quot;highlighter-rouge&quot;&gt;/holiday/new-year/musical/&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;/occasion/birthday/musical/&lt;/code&gt; would be replaced in a flat URL structure with a single URL – &lt;code class=&quot;highlighter-rouge&quot;&gt;/musical/&lt;/code&gt;. Does that address contain content from both categories? Neither? How are search engines (or users) to interpret that content?&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; It is speculated that Google practices “URL stripping”, whereby “directories” are systematically removed from the path and crawled to discover new content. Therefor, it is important for category pages to contain a relevant description and links to its contents. “Virtual directories” which return no content or a 404 status code when queried directly may result in extra crawl errors.&lt;/p&gt;

&lt;h3 id=&quot;analytics&quot;&gt;Analytics&lt;/h3&gt;

&lt;p&gt;It is essential to analyze organic traffic by sections of a site through content drill down reports available in analytics tools; these tools rely on structured URLs to monitor user flow.&lt;/p&gt;

&lt;p&gt;Gathering data on indexed pages is made easier with hierarchical URLs, enabling Google queries like &lt;code class=&quot;highlighter-rouge&quot;&gt;site:example.com/category/&lt;/code&gt;. As noted below, Google has limits on how many results can be returned with a single query. For sites with many URLs, refining a indexing queries to subcategories can workaround these limitations.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; Google &lt;a href=&quot;https://support.google.com/gsa/answer/4411411#search&quot;&gt;limits the maximum number of search results&lt;/a&gt; for a single query to 1000 results.&lt;/p&gt;

&lt;h2 id=&quot;migration&quot;&gt;Migration&lt;/h2&gt;

&lt;p&gt;When migrating to a new URL structure, effective redirection is critical maintaining conversions across direct and organic traffic. Redirection is made easier with hierarchical URLs because regular expressions can be used to redirect entire sections of URLs to a new location.&lt;/p&gt;

&lt;p&gt;In any case, migrating a site’s URL structure typically does have a temporary negative impact on organic traffic – usually a 10–20% drop in organic traffic can be expected during the first 1–3 months after a migration has occurred.&lt;/p&gt;

&lt;h2 id=&quot;product-urls&quot;&gt;Product URLs&lt;/h2&gt;

&lt;p&gt;Product URLs are not inherently hierarchical, as a single product may appear in multiple categories. For optimal UX, the “virtual breadcrumb” URL structure should be preserved by using hierarchical URLs when navigating to products via categories and subcategories. However, product URLs should also appear at the root level (as a flat URL), and all hierarchical product URLs should be canonicalized to the flat URL, so the shorter URL appears in SERPs without creating duplicate content issues.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Crafting 301 Redirects</title>
   <link href="https://joeyhoer.com/crafting-301s-2fa5ea87"/>
   <updated>2016-01-15T09:51:08-05:00</updated>
   <id>https://joeyhoer.com/crafting-301s</id>
   <content type="html">&lt;p&gt;When launching a website on a new platform or when changing the information architecture of a website, it is usually necessary to configure redirects to ensure users can locate the content they expect even when accessing the website via legacy URLs. These redirects also instruct search engines to reindex content at a new location and transfer page ranking.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If you are migrating a website to a new domain or subdomain, be sure to use &lt;a href=&quot;https://support.google.com/webmasters/answer/83106&quot;&gt;Google’s Change of Address&lt;/a&gt; tool in addition to configuring the appropriate redirects.&lt;/p&gt;

&lt;h2 id=&quot;different-types-of-redirects&quot;&gt;Different Types of Redirects&lt;/h2&gt;

&lt;p&gt;Redirects are &lt;a href=&quot;http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html&quot;&gt;HTTP status codes&lt;/a&gt; which instruct the browser to perform a certain action. There are &lt;a href=&quot;https://httpstatuses.com&quot;&gt;many different status codes&lt;/a&gt; which fall into different classes. Redirect statuses follow the pattern 3XX, with the primary status codes for redirects being 301 (permanent redirect) and 302 (temporary redirect), though there are other statuses – notably the 307 (temporary redirect) and 308 (permanent redirect) – that specify the user agent must not change the HTTP method used in the original request.&lt;/p&gt;

&lt;h3 id=&quot;permanent-redirects&quot;&gt;Permanent Redirects&lt;/h3&gt;

&lt;p&gt;The 301 (and 308) status code instructs the browser that the content of a resource has been moved &lt;em&gt;permanently&lt;/em&gt; to a new location. In other words, the browser is told that it &lt;em&gt;should no longer request the original location&lt;/em&gt;, instead using the new location for all future requests.&lt;/p&gt;

&lt;p&gt;When a search engine robot encounters a permanent redirect it replaces the indexed URL with the new URL and transfers (&lt;a href=&quot;https://www.youtube.com/watch?v=Filv4pP-1nw&quot;&gt;a percentage&lt;/a&gt;) of PageRank to the new URL.&lt;/p&gt;

&lt;p&gt;When a web browser encounters a permanent redirect it caches the redirect and  will not attempt to request the original location again, instead pointing to the new location, until the cache is cleared.&lt;/p&gt;

&lt;p&gt;It is possible to unset a cached permanent redirect by re-redirecting back to the original URL from the destination URL. For this reason, be cautious when performing permanent redirects to external targets, as it will be impossible to unset the redirect.&lt;/p&gt;

&lt;h3 id=&quot;temporary-redirects&quot;&gt;Temporary Redirects&lt;/h3&gt;

&lt;p&gt;The 302 (and 307) status code instructs the browser that the content of a resource has been moved &lt;em&gt;temporarily&lt;/em&gt; to a new location. In other words, because this redirect may be altered occasionally, the browser &lt;em&gt;should continue to request the original location&lt;/em&gt; for future requests.&lt;/p&gt;

&lt;p&gt;Interestingly temporary redirects receive some &lt;a href=&quot;https://www.mattcutts.com/blog/seo-advice-discussing-302-redirects/&quot;&gt;perhaps unexpected treatment by search engines&lt;/a&gt;: “for on-domain [temporary] redirects… search engines will usually [index] the shorter URL” because “normal users usually like short, clean URLs.”&lt;/p&gt;

&lt;p&gt;It is &lt;a href=&quot;https://twitter.com/JohnMu/status/712541291595304964&quot;&gt;a common misconception&lt;/a&gt; that temporary redirects do not transfer PageRank, &lt;a href=&quot;https://plus.google.com/u/0/+JohnMueller/posts/PY1xCWbeDVC#z13hzxpyqmzyzzr4a04cfnlq2ofluhkzn5w#1454483527409221&quot;&gt;they do&lt;/a&gt;. Though it has also been suggested that 302 redirects transfer less PageRank than 301 redirects (which may be true).&lt;/p&gt;

&lt;p&gt;Keep in mind that search engines are constantly updating their algorithms to deliver the best results. More than anything, they want webmasters to build websites that provide a great user experience and valuable content. Scheming to acquire more “link juice” is not a valid argument for using an improper redirect; in fact, &lt;a href=&quot;http://www.thesempost.com/google-removing-pagerank-from-toolbar/&quot;&gt;Google has taken steps in the past to reduce visibility into PageRank&lt;/a&gt; to “avoid confusing users and webmasters about the &lt;a href=&quot;https://webmasters.googleblog.com/2011/06/beyond-pagerank-graduating-to.html&quot;&gt;significance of the metric&lt;/a&gt;.” As Matt Cutts says “use whatever is best for your purposes.”&lt;/p&gt;

&lt;h2 id=&quot;locating-urls&quot;&gt;Locating URLs&lt;/h2&gt;

&lt;p&gt;Before writing a redirect you must know three things:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;The source URL&lt;/li&gt;
  &lt;li&gt;The destination (or target) URL&lt;/li&gt;
  &lt;li&gt;The redirect status&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;acquiring-source-urls&quot;&gt;Acquiring source URLs&lt;/h3&gt;

&lt;p&gt;The easiest way to acquire a list of known URLs is to locate a website’s sitemap(s). The following script can be used to search for &lt;a href=&quot;http://www.sitemaps.org/protocol.html#submit_robots&quot;&gt;sitemap declarations in the robots.txt file&lt;/a&gt; and by testing for popular sitemap filenames.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Locate sitemap path for a given domain&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# @param [url] Domain name to search&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;find_sitemap&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;# Get sitemap entries from a `robots.txt` file&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;sitemap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-sL&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/robots.txt&quot;&lt;/span&gt; | awk &lt;span class=&quot;s1&quot;&gt;'tolower($1) ~ /^sitemap/ {print $2}'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$sitemap&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# Try different combinations of capitalization and punctuation&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# sitemap site_map site-map&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-sLIw&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%{http_code} %{url_effective}&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/sitemap.xml&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; /dev/null&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$output&lt;/span&gt; | awk &lt;span class=&quot;s1&quot;&gt;'{print $1}'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; 200 &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
      &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sitemap&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$output&lt;/span&gt; | awk &lt;span class=&quot;s1&quot;&gt;'{print $2}'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else
      &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;No sitemap found&quot;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return &lt;/span&gt;1
    &lt;span class=&quot;k&quot;&gt;fi
  fi
  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sitemap&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

find_sitemap &lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Alternatively, if the site has a Google Webmaster Tools account configured, you may check the &lt;a href=&quot;https://www.google.com/webmasters/tools/sitemap-list&quot;&gt;Crawl Sitemap&lt;/a&gt; section to see if any sitemaps are known to Google.&lt;/p&gt;

&lt;p&gt;If none of the above methods have located a valid sitemap, you may need to resort to searching Google for possible sitemaps. The search queries below may assist in locating a sitemap:&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;site:example.com filetype:xml inurl:sitemap

site:example.com filetype:xml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you still cannot locate a valid sitemap file, you may need to &lt;a href=&quot;https://github.com/NikolaiT/GoogleScraper&quot;&gt;build a list based on search engine indexes&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;extracting-urls-from-a-sitemap&quot;&gt;Extracting URLs from a Sitemap&lt;/h3&gt;

&lt;p&gt;In addition to a list of URLs, sitemaps include other information, unnecessary for the purposes of constructing redirects. To extract URLs from a sitemap, use the following script:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Parse URLs out of a `sitemap.xml` file&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# This will follow nested sitemap URLs&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# @param [string] File or URL or sitemap&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;parse_sitemap&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;# Check is sitemap is local or remote&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;~ ^https?:// &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;http_code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-sLIw&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;%{http_code}&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; /dev/null&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$http_code&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; 200 &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
      return &lt;/span&gt;1
    &lt;span class=&quot;k&quot;&gt;fi
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;net&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'--net'&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;# Must match the XML namespace to work correctly&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'http://www.sitemaps.org/schemas/sitemap/0.9'&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;# Get all URLs - remove blank line if no URLs are found&lt;/span&gt;
  xml sel &lt;span class=&quot;nv&quot;&gt;$net&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-N&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'//x:url/x:loc'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | sed &lt;span class=&quot;s1&quot;&gt;'/^\s*$/d'&lt;/span&gt;
  &lt;span class=&quot;c&quot;&gt;# Recursively parse embedded sitemaps&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;sitemaps&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;xml sel &lt;span class=&quot;nv&quot;&gt;$net&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-N&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'//x:sitemap/x:loc'&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | sed &lt;span class=&quot;s1&quot;&gt;'/^\s*$/d'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sitemaps&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
    while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; sitemap&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
      &lt;/span&gt;parse_sitemap &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sitemap&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$sitemaps&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

parse_sitemap &lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; This script requires &lt;a href=&quot;http://xmlstar.sourceforge.net&quot;&gt;XMLStarlet&lt;/a&gt;, and will descend recursively into nested sitemaps.&lt;/p&gt;

&lt;h2 id=&quot;writing-redirects&quot;&gt;Writing Redirects&lt;/h2&gt;

&lt;p&gt;Once a list of source URLs is obtained, it’s time to begin mapping to the destination URL(s).&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; This section describes the different modules and directives that may be used to author redirects in &lt;em&gt;Apache&lt;/em&gt;; be aware of your server type and version.&lt;/p&gt;

&lt;p&gt;There are two Apache modules that may be used to construct redirects:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://httpd.apache.org/docs/current/mod/mod_alias.html&quot;&gt;mod_alias&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://httpd.apache.org/docs/current/mod/mod_rewrite.html&quot;&gt;mod_rewrite&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These modules provide three directives that can be used for redirection; &lt;code class=&quot;highlighter-rouge&quot;&gt;mod_alias&lt;/code&gt; provides &lt;code class=&quot;highlighter-rouge&quot;&gt;Redirect&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;RedirectMatch&lt;/code&gt; while &lt;code class=&quot;highlighter-rouge&quot;&gt;mod_rewrite&lt;/code&gt; provides &lt;code class=&quot;highlighter-rouge&quot;&gt;RewriteRule&lt;/code&gt;. As noted by the Apache documentation for &lt;a href=&quot;http://httpd.apache.org/docs/current/mod/mod_alias.html#preamble&quot;&gt;mod_alias&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;mod_alias&lt;/code&gt; is designed to handle simple URL manipulation tasks. For more complicated tasks … use the tools provided by &lt;code class=&quot;highlighter-rouge&quot;&gt;mod_rewrite&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following rules may be applied when selecting the appropriate directive for a redirect:&lt;/p&gt;

&lt;dl class=&quot;compact ul&quot;&gt;
  &lt;dt&gt;Redirect&lt;/dt&gt;
  &lt;dd&gt;Simple one-to-one redirect mapping.&lt;/dd&gt;
  &lt;dt&gt;RedirectMatch&lt;/dt&gt;
  &lt;dd&gt;Intermediate redirect mapping supporting regular expressions.&lt;/dd&gt;
  &lt;dt&gt;RewriteRule&lt;/dt&gt;
  &lt;dd&gt;Advanced redirect mapping supporting regular expressions, conditionals and access to the query string.&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Apache has also published helpful documentation on &lt;a href=&quot;https://httpd.apache.org/docs/current/rewrite/avoid.html&quot;&gt;when not to use &lt;code class=&quot;highlighter-rouge&quot;&gt;mod_rewrite&lt;/code&gt;&lt;/a&gt;, which echos the above:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;[S]imple redirection of one URL, or a class of URLs, to somewhere else, should be accomplished using [the Redirect and RedirectMatch] directives rather than RewriteRule. RedirectMatch allows you to include a regular expression in your redirection criteria, providing many of the benefits of using RewriteRule.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3 id=&quot;the-redirect-directive&quot;&gt;The Redirect directive&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;http://httpd.apache.org/docs/current/mod/mod_alias.html#redirect&quot;&gt;Redirect directive&lt;/a&gt; is extremely simplistic; it maps one URL to another:&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;Redirect&lt;/span&gt; 301 &quot;/source&quot; &quot;/destination&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; In the example above (and with the other directives mentioned below), the redirect’s HTTP status code argument may be omitted. If no status code is defined, the redirect will be a 302 temporary redirect.&lt;/p&gt;

&lt;p&gt;There are a few keywords that may be used in place of a numeric status code, which may improve readability. These keywords are limited however, and numeric status codes offer more flexibility and allow for greater consistency when a number of different redirect statuses are defined.&lt;/p&gt;

&lt;p&gt;To improve clarity, always include a numeric status code with all redirect directives.&lt;/p&gt;

&lt;h3 id=&quot;the-redirectmatch-directive&quot;&gt;The RedirectMatch directive&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;http://httpd.apache.org/docs/current/mod/mod_alias.html#redirectmatch&quot;&gt;RedirectMatch directive&lt;/a&gt; expands the utility of Redirect while maintaining it’s elegant simplicity. Any valid Redirect directive is also a valid RedirectMatch.&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 301 &quot;/source&quot; &quot;/destination&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The real power of RedirectMatch derives from its ability to interpret &lt;em&gt;regular expressions&lt;/em&gt; (commonly referred to as &lt;em&gt;regex&lt;/em&gt;). To master RedirectMatch (or redirection in general), learn regular expressions. Understanding even the basics of regex can be extremely valuable when redirecting a large number of URLs.&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Remove an extension from all URLs&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 301 &quot;(.*)\.html&quot; &quot;$1&quot;

&lt;span class=&quot;c&quot;&gt;# Move/rename a category&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 301 &quot;/old-category/(.*)&quot; &quot;/new-category/$1&quot;

&lt;span class=&quot;c&quot;&gt;# Redirect subdirectory to subdomain&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RedirectMatch&lt;/span&gt; 301 &quot;^/(blog)/(.*)$&quot; &quot;http://$1.example.com/$2&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This illustrates the power of using RedirectMatch with regular expressions.&lt;/p&gt;

&lt;p&gt;Nevertheless, there is still much that RedirectMatch cannot accomplish.&lt;/p&gt;

&lt;h3 id=&quot;the-redirectrule-directive&quot;&gt;The RedirectRule directive&lt;/h3&gt;

&lt;p&gt;The &lt;a href=&quot;http://httpd.apache.org/docs/current/mod/mod_rewrite.html#rewriterule&quot;&gt;RedirectRule directive&lt;/a&gt; uses a more complex syntax to accomplish redirection. This added complexity affords much more flexibility and power.&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;RedirectRule /source /destination [R=301,L]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Some servers may not have &lt;code class=&quot;highlighter-rouge&quot;&gt;mod_rewrite&lt;/code&gt; enabled. To avoid 500 errors wrap &lt;code class=&quot;highlighter-rouge&quot;&gt;mod_rewrite&lt;/code&gt; directives in an &lt;code class=&quot;highlighter-rouge&quot;&gt;IfModule&lt;/code&gt; section. Also, ensure that the RewriteEngine directive has been set to “On”, otherwise the RedirectRule directives will have no effect.&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;IfModule&lt;/span&gt;&lt;span class=&quot;sr&quot;&gt; mod_rewrite.c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;  &lt;span class=&quot;nc&quot;&gt;RewriteEngine&lt;/span&gt; On
  &lt;span class=&quot;c&quot;&gt;# …&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;IfModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The power of RewriteRule lies in its ability to be combined with conditional statements (RewriteCond) and access server variables, environment variables, HTTP headers, and time stamps.&lt;/p&gt;

&lt;div class=&quot;language-apache highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-apache&quot; data-lang=&quot;apache&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Redirect insecure, non-www to secure, www&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{HTTPS} !on [OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{HTTP_HOST} !^www\. [NC]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{HTTP_HOST} ^(?:www\.)?(.*) [NC]
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; (.*) https://www.%1/$1 [R=301,L]

&lt;span class=&quot;c&quot;&gt;# Collapse multiple slashes&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Note: The &quot;$0&quot; in the substitution string matches&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# the entire contents of the matched pattern,&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# regardless of grouping&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{THE_REQUEST} //
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; .* /$0 [R=301,NE]

&lt;span class=&quot;c&quot;&gt;# Parse data from a query string&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# and remove the query string from the URL&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Note: In Apache 2.4 or later, the &quot;QSD&quot; option (qsdiscard)&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# can be used to remove the query string opposed to&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# appending a &quot;?&quot; the the substitution string&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{QUERY_STRING} page=([^&amp;amp;]+)
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; ^ /%1? [R=301,L]

&lt;span class=&quot;c&quot;&gt;# Redirect certain devices by user agent&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{HTTP_USER_AGENT} &quot;i(phone|pad|pod)&quot; [NC]
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; ^ http://m.example.com/ [R=302,L]

&lt;span class=&quot;c&quot;&gt;# Serve pre-compressed gzipped or bred (Brotli) assets&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Prefers Brotli compressed assets&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{HTTP:Accept-Encoding} \b(br)\b [OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{HTTP:Accept-Encoding} \b(gz)ip\b
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_FILENAME} !.+\.%1$
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_FILENAME}.%1 -f [OR]
&lt;span class=&quot;nc&quot;&gt;RewriteCond&lt;/span&gt; %{REQUEST_FILENAME}.%1 -l
&lt;span class=&quot;nc&quot;&gt;RewriteRule&lt;/span&gt; (.*) $1.%1 [QSA,L]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; Be aware that not all directives are interpreted sequentially due to &lt;a href=&quot;http://httpd.apache.org/docs/current/developer/hooks.html#hooking-order&quot;&gt;hooks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As &lt;a href=&quot;http://httpd.apache.org/docs/current/rewrite/avoid.html#redirect&quot;&gt;the Apache documentation&lt;/a&gt; notes:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The use of &lt;code class=&quot;highlighter-rouge&quot;&gt;RewriteRule&lt;/code&gt; … may be appropriate if there are other &lt;code class=&quot;highlighter-rouge&quot;&gt;RewriteRule&lt;/code&gt; directives in the same scope. [W]hen there are &lt;code class=&quot;highlighter-rouge&quot;&gt;Redirect&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&gt;RewriteRule&lt;/code&gt; directives in the same scope, the &lt;code class=&quot;highlighter-rouge&quot;&gt;RewriteRule&lt;/code&gt; directives will run first, regardless of the order of appearance in the configuration file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Placing a Redirect directive before a RewriteRule directive does not mean it will be executed first. &lt;code class=&quot;highlighter-rouge&quot;&gt;mod_rewrite&lt;/code&gt; is loaded before &lt;code class=&quot;highlighter-rouge&quot;&gt;mod_alias&lt;/code&gt; because of hooks, so RewriteRule directives are executed before Redirect and RedirectMatch directives.&lt;/p&gt;

&lt;h2 id=&quot;testing-redirects&quot;&gt;Testing Redirects&lt;/h2&gt;

&lt;p&gt;While writing redirects, it is helpful to use a command line tool for testing. This avoids browser caching.&lt;/p&gt;

&lt;p&gt;Because a percentage of PageRank “dissipates” with each redirect, and because each redirect contributes to a slower time to first byte, the total number of redirects between the source URL and destination URL (redirect “hops”) should be kept to a minimum. Whenever possible, each redirect should be accomplished in only one hop. The following script will display all the redirect hops of a single URL:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Display all redirects (and their status code) between&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# a given URL and the &quot;effective URL&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# @param [url] The URL to crawl&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;redirects&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-Iso&lt;/span&gt; /dev/null &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'%{http_code} %{redirect_url}'&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$url&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
    &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;http_code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | awk &lt;span class=&quot;s1&quot;&gt;'{print $1}'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$http_code&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$url&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | awk &lt;span class=&quot;s1&quot;&gt;'{print $2}'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

redirects &lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;While the script above is helpful in a micro-sense – allowing developers to test individual URLs – the script below is more helpful in the macro-sense – allowing developers to test a large number of URLs at once.&lt;/p&gt;

&lt;p&gt;Given a list of URLs to test, this script will display each URL in a file alongside the effective URL and it’s status code in CSV format.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Test URLs from a list&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# Prints HTTP status code, redirect hops, source URL, and effective URL&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# in CSV format&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# @param [file] List of URLs to test&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# @param [string] Base URL to test URLs against&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;##&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;test_urls&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;base_url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$base_url&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# Remove protocol and domain name and&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# replace with new base URL&lt;/span&gt;
    sed &lt;span class=&quot;nt&quot;&gt;-E&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;s%^https?://[^/]*%%g&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/dev/stdin&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | xargs &lt;span class=&quot;nt&quot;&gt;-I&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      curl &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; /dev/null &lt;span class=&quot;nt&quot;&gt;--head&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-sLw&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;%{http_code},%{num_redirects},&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;base_url&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{},%{url_effective}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;n&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;base_url&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;{}&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;else
    &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/dev/stdin&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; | xargs &lt;span class=&quot;nt&quot;&gt;-I&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      curl &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; /dev/null &lt;span class=&quot;nt&quot;&gt;--head&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-sLw&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
      &lt;span class=&quot;s2&quot;&gt;&quot;%{http_code},%{num_redirects},{},%{url_effective}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\\&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;n&quot;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;{}&quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

test_urls &lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Tip:&lt;/strong&gt; When analyzing the data generated by the script above, filter content by the status codes. Unsuccessful requests (≠ 200) – particularly 404 – indicate an error which may need to be addressed with redirects.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Text Resize Widgets</title>
   <link href="https://joeyhoer.com/text-resize-widgets-af274410"/>
   <updated>2015-04-08T13:25:00-04:00</updated>
   <id>https://joeyhoer.com/text-resize-widgets</id>
   <content type="html">&lt;p&gt;Text resize widgets are redundant artifacts of the 20th century web, many being poorly designed an poorly implemented. While these widgets were intended to assist with accessibility, in reality, they not only fail to improve accessibility, but they also detract from site design and negatively affect site performance.&lt;/p&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;antique-accessibility&quot;&gt;Antique Accessibility&lt;/h2&gt;

&lt;p&gt;In 1997, the W3C launched the &lt;a href=&quot;http://www.w3.org/WAI/&quot;&gt;Web Accessibility Initiative&lt;/a&gt;. Designed to educate designers and developers on how to improve accessibility on the web for users with disabilities, the WAI encouraged many designers and developers to implement their own solutions to solve accessibility needs. As the initiative grew, browsers began implementing their own solutions for users with disabilities. Opera 2.1, released in Dec. 1996, was the first browser to implement a “Zoom” feature, but it took 10 years for Microsoft to implement browser zoom, which was included in IE7. Today, all modern browsers support some variation of page zoom – with some browsers offering additional options like “Zoom Text Only”.&lt;/p&gt;

&lt;h2 id=&quot;accessibility-issues&quot;&gt;Accessibility Issues&lt;/h2&gt;

&lt;p&gt;The main problem with text resize widgets is their inherent inaccessibility. Let’s consider, for a moment, whom text resize widgets might benefit. The first group that comes to mind is users with poor eyesight. Now, let’s examine what happens when a user with this type of disability visits a page that utilizes a text resize widget. Where should they direct their attention if they are unable to read the content of the page? The answer obviously being the text resize widget. But where exactly is the text resize widget? This varies from site to site, occasionally even from page to page, and besides, the visitor has a disability – &lt;em&gt;poor eyesight&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Depending on the size, contrast, prominence and clarity of purpose of the widget, it can be very difficult for someone with accessibility issues to identify. Because these widgets rely on the users ability to locate them on the page, they are themselves &lt;em&gt;inaccessible&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Not only does the text resize widget make the site less accessible for users with poor vision, it also diminishes accessibility and usability for a number of other users. As &lt;a href=&quot;http://webaim.org/blog/web-accessibility-preferences-are-for-sissies/&quot;&gt;noted by Jared Smith on Web Accessibility In Mind&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Users who are blind generally don’t care how big the text is, yet they must listen to and navigate through the widget controls on every page. Users with motor disabilities who use only the keyboard must also ‘tab’ through these controls while gaining no benefit from them. Users with auditory disabilities gain no benefit. Users with cognitive disabilities must handle the additional cognitive load of these controls (what does a stack of increasingly sized A’s mean anyway?). And most notably, users with adequate vision gain no utility from the widget (again, assuming your default font size is adequate), yet are still presented with the control on each and every page.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In fact, the number of users who potentially suffer from the negative side effects of poorly implemented text resize widgets far outnumbers the users who might potentially benefit. When &lt;a href=&quot;http://www.joedolson.com/articles/2008/02/developing-an-effective-text-resizing-widget/&quot;&gt;implemented properly&lt;/a&gt;, these widgets rely on additional JavaScript, markup, styling, and often images in addition to cookies which are stored on a users machine to save preferences across the site and between visits. These aspects all contribute a negative impact to overall site performance, degrading the experience of every visitor to the site.&lt;/p&gt;

&lt;h2 id=&quot;intelligent-design&quot;&gt;Intelligent Design&lt;/h2&gt;

&lt;p&gt;To minimize overhead and improve the quality of each visitor’s experience, the target audience should always be carefully considered during the design phase – text sizes should be selected accordingly. Smith argues that the addition of a text resize widget is an admission of error by the designer – “either they are forcing the user to account for a poor design or usability decision, or they are too indecisive to make the decision to begin with and thus place the burden to decide upon the user.”&lt;/p&gt;

&lt;p&gt;By including a text resize widget on a site, a designer is making two assumptions:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;a significant number of site visitors may require larger font sizes&lt;/li&gt;
  &lt;li&gt;visitors are not savvy enough to establish a larger default font setting&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Under these conditions a designer could, hypothetically, create a text resize widget that takes prominence, is clearly identifiable and appropriately size, that when implemented correctly could improve accessibility for a very small percentage of users. However, the designer must &lt;a href=&quot;http://accessites.org/site/2008/08/wrangling-widgets/&quot;&gt;weigh&lt;/a&gt; the &lt;a href=&quot;http://www.connectioncafe.com/posts/2009/may/the-text-resizer-debate.html&quot;&gt;sacrifices&lt;/a&gt; in design, accessibility, and usability for every other site visitor.&lt;/p&gt;

&lt;h2 id=&quot;the-bottom-line&quot;&gt;The Bottom Line&lt;/h2&gt;

&lt;p&gt;There are much better ways to improve accessibility than using text resize widgets, for example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;size text with relative units (like &lt;code class=&quot;highlighter-rouge&quot;&gt;ems&lt;/code&gt;) instead of pixels to ensure that built-in zoom functions properly&lt;/li&gt;
  &lt;li&gt;leverage appropriate foreground-background contrast to improve readability&lt;/li&gt;
  &lt;li&gt;select a font size which suits the needs of the target audience to improve the quality of a visit&lt;/li&gt;
  &lt;li&gt;educate less-savvy users on how to use built-in functionality to make the web more progressive&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>CSS Font Units</title>
   <link href="https://joeyhoer.com/css-font-units-b37ee37b"/>
   <updated>2014-02-26T22:33:18-05:00</updated>
   <id>https://joeyhoer.com/css-font-units</id>
   <content type="html">&lt;p&gt;When Marshall McLuhan coined the phrase “the medium is the message”, he referred to a concept which lends itself well to modern day web design. He contended that the medium in which a message is delivered affects how that message is perceived. In the same way, today’s web developers must select the appropriate font units for the medium where their work is presented. The choice of font units has a literal impact on how the message is perceived. Therefore, it is important for designers and developers alike to be aware of what units are available and when to use them.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;According to the W3 Consortium, &lt;em&gt;lengths&lt;/em&gt; refer to horizontal or vertical measurements, and there are two types of length units: &lt;strong&gt;relative&lt;/strong&gt; and &lt;strong&gt;absolute&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.w3.org/TR/css-values-3/#relative-lengths&quot;&gt;Relative units&lt;/a&gt; are:&lt;/p&gt;

&lt;dl class=&quot;compact ul&quot;&gt;
  &lt;dt&gt;em&lt;/dt&gt;
  &lt;dd&gt;the &lt;code class=&quot;highlighter-rouge&quot;&gt;font-size&lt;/code&gt; of the relevant font&lt;/dd&gt;
  &lt;dt&gt;ex&lt;/dt&gt;
  &lt;dd&gt;the &lt;code class=&quot;highlighter-rouge&quot;&gt;x-height&lt;/code&gt; of the relevant font&lt;/dd&gt;
  &lt;dt&gt;ch&lt;/dt&gt;
  &lt;dd&gt;width of the “0” (ZERO, U+0030) glyph in the element’s font&lt;/dd&gt;
  &lt;dt&gt;rem&lt;/dt&gt;
  &lt;dd&gt;font size of the root element&lt;/dd&gt;
  &lt;dt&gt;vw&lt;/dt&gt;
  &lt;dd&gt;1% of viewport’s width&lt;/dd&gt;
  &lt;dt&gt;vh&lt;/dt&gt;
  &lt;dd&gt;1% of viewport’s height&lt;/dd&gt;
  &lt;dt&gt;vmin&lt;/dt&gt;
  &lt;dd&gt;1% of viewport’s smaller dimension&lt;/dd&gt;
  &lt;dt&gt;vmax&lt;/dt&gt;
  &lt;dd&gt;1% of viewport’s larger dimension&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;&lt;a href=&quot;https://www.w3.org/TR/css-values-3/#absolute-lengths&quot;&gt;Absolute units&lt;/a&gt; are fixed in relation to each other:&lt;/p&gt;

&lt;dl class=&quot;compact ul&quot;&gt;
  &lt;dt&gt;in&lt;/dt&gt;
  &lt;dd&gt;inches — &lt;code class=&quot;highlighter-rouge&quot;&gt;1in&lt;/code&gt; is equal to &lt;code class=&quot;highlighter-rouge&quot;&gt;96px&lt;/code&gt;&lt;/dd&gt;
  &lt;dt&gt;cm&lt;/dt&gt;
  &lt;dd&gt;centimeters – 1in/2.54 = &lt;code class=&quot;highlighter-rouge&quot;&gt;37.795px&lt;/code&gt;&lt;/dd&gt;
  &lt;dt&gt;mm&lt;/dt&gt;
  &lt;dd&gt;millimeters – 1/10th of &lt;code class=&quot;highlighter-rouge&quot;&gt;1cm&lt;/code&gt; = &lt;code class=&quot;highlighter-rouge&quot;&gt;3.779px&lt;/code&gt;&lt;/dd&gt;
  &lt;dt&gt;q&lt;/dt&gt;
  &lt;dd&gt;quarter-millimeters – 1/40th of &lt;code class=&quot;highlighter-rouge&quot;&gt;1in&lt;/code&gt; = &lt;code class=&quot;highlighter-rouge&quot;&gt;0.944px&lt;/code&gt;&lt;/dd&gt;
  &lt;dt&gt;pt&lt;/dt&gt;
  &lt;dd&gt;points — 1/72nd of &lt;code class=&quot;highlighter-rouge&quot;&gt;1in&lt;/code&gt; = &lt;code class=&quot;highlighter-rouge&quot;&gt;1.333px&lt;/code&gt;&lt;/dd&gt;
  &lt;dt&gt;pc&lt;/dt&gt;
  &lt;dd&gt;picas — 1/6th of &lt;code class=&quot;highlighter-rouge&quot;&gt;1in&lt;/code&gt; = &lt;code class=&quot;highlighter-rouge&quot;&gt;16px&lt;/code&gt;&lt;/dd&gt;
  &lt;dt&gt;px&lt;/dt&gt;
  &lt;dd&gt;pixels — one reference pixel&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;The CSS specification states, “absolute units are mainly useful when the output environment is known.” This is an updated paraphrasing of the old spec that stated, “absolute length units are only useful when the physical properties of the output medium are known.” With the advent of mobile devices the variety of screen sizes, resolutions, and orientations is more diverse than ever. A developer can seldom be certain of the physical properties of the output medium. To accommodate these discrepancies, the W3 Consortium recommends that developers rely on relative units to create flexible designs that render well on an assortment of displays.&lt;/p&gt;

&lt;h2 id=&quot;the-incredible-em&quot;&gt;The Incredible Em&lt;/h2&gt;

&lt;p&gt;By using ems, developers can improve accessibility, maintain flexible layouts and create code that is more readable. Ems are particularly well suited for font sizing on the web because an em is a measurement of typography and a relative unit. 1em is equal to the current font size; font size is an inherited property—i.e. it is passed from a parent element to its children.&lt;/p&gt;

&lt;p&gt;The web is all about choice and accessibility. If an end user chooses to set their default font size to a value larger than 16px (which is most all browsers default), they should receive content that has been appropriately sized to fit their needs. This is particularly important for users with poor eyesight and small monitors. The following example demonstrates what occurs when absolute units are used:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-css&quot; data-lang=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;16px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;24px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;12px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the example above, if an end user decides to increase the default font size to &lt;code class=&quot;highlighter-rouge&quot;&gt;20px&lt;/code&gt;, the &lt;code class=&quot;highlighter-rouge&quot;&gt;font-size&lt;/code&gt; will not change because pixels are absolute. However, if the units are changed to ems something very different happens:&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-css&quot; data-lang=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1em&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;h1&lt;/span&gt;  &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2em&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;p&lt;/span&gt;   &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;.75em&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;By default, the above example will look identical to the one before it, but when an end user opts for a larger default font size of &lt;code class=&quot;highlighter-rouge&quot;&gt;20px&lt;/code&gt;, the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; will receive a calculated font size of &lt;code class=&quot;highlighter-rouge&quot;&gt;40px&lt;/code&gt;, and the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;p&amp;gt;&lt;/code&gt; will receive a font size of &lt;code class=&quot;highlighter-rouge&quot;&gt;15px&lt;/code&gt;. Admittedly, the example above is somewhat archaic. &lt;a href=&quot;https://www.davidbcalhoun.com/2010/does-it-still-make-sense-to-use-em-rather-than-px/&quot;&gt;Most modern browsers &lt;em&gt;can&lt;/em&gt; effectively scale pixels in the same way they scale ems&lt;/a&gt;, but he also points out their practical use in flexible designs.&lt;/p&gt;

&lt;p&gt;Fluid, elastic and responsive designs rely heavily on ems to create scalable sites in addition to &lt;code class=&quot;highlighter-rouge&quot;&gt;@media&lt;/code&gt; queries which can be used to target different devices. Developers who make use of ems can easily improve text readability on smaller screens by increasing the font size on the &lt;code class=&quot;highlighter-rouge&quot;&gt;body&lt;/code&gt; and allowing it to cascade.&lt;/p&gt;

&lt;p&gt;This makes code cleaner, because stylesheets can be smaller and more readable. A developer need not know what the actual display size is, so long as the ratio between elements is appropriate.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; There is a bug in older versions of Internet Explorer that “unacceptably exaggerate the smallness and largeness of the resized text,” according to A List Apart. The solution is simple, elegant, and is included in many CSS resets by default. Simply set &lt;code class=&quot;highlighter-rouge&quot;&gt;body {font-size:100%;}&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;bonus-points&quot;&gt;Bonus Points&lt;/h2&gt;

&lt;p&gt;Because absolute units are particularly well suited to applications where the size of the medium is fairly consistent, points are best used in print design. Print design should not be neglected when designing for the web. It’s a good idea to target the printed page by making use of @media print queries to hide interactive elements such as site navigation, reset text and background colors, and redeclare font sizes in points to allow for more control over the printed page in an effort to conserve paper and ink while creating a better overall presentation.&lt;/p&gt;

&lt;p class=&quot;note&quot;&gt;&lt;strong&gt;Note:&lt;/strong&gt; Because ems are relative to the current font size, they can also be effectively used in print style sheets. It is recommended that developers declare &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;body&amp;gt;&lt;/code&gt; size in points and allow the document to cascade.&lt;/p&gt;

&lt;h2 id=&quot;unitless-bliss&quot;&gt;Unitless Bliss&lt;/h2&gt;

&lt;p&gt;The CSS line-height property can accept unitless values, and should always be declared as such because it makes for cleaner, more readable style sheets. Unitless line height is relative and is calculated as a ratio of the current font size for each element.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-css&quot; data-lang=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1em&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;li&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the above example, the line height for each element is inherited from the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;, because &lt;code class=&quot;highlighter-rouge&quot;&gt;em&lt;/code&gt;s are relative to the element on which they are defined the calculated line-height for the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; is &lt;code class=&quot;highlighter-rouge&quot;&gt;15px&lt;/code&gt;. This value is inherited by the children, so both the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;li&amp;gt;&lt;/code&gt; and the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;small&amp;gt;&lt;/code&gt; have a line-height of &lt;code class=&quot;highlighter-rouge&quot;&gt;15px&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Unitless values, on the other hand, are inherited as a “raw number”. By modifying the above CSS to use unitless values, the line-height of each child element is set &lt;em&gt;relative to itself&lt;/em&gt; and it’s &lt;code class=&quot;highlighter-rouge&quot;&gt;font-size&lt;/code&gt;, &lt;em&gt;even if its &lt;code class=&quot;highlighter-rouge&quot;&gt;font-size&lt;/code&gt; has changed&lt;/em&gt;.&lt;/p&gt;

&lt;div class=&quot;language-css highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight language-css&quot; data-lang=&quot;css&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;ul&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;15px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;li&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10px&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this example the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; still receives a line height of &lt;code class=&quot;highlighter-rouge&quot;&gt;15px&lt;/code&gt;, but now the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;li&amp;gt;&lt;/code&gt; receives a line height of &lt;code class=&quot;highlighter-rouge&quot;&gt;10px&lt;/code&gt;, and the &lt;code class=&quot;highlighter-rouge&quot;&gt;&amp;lt;small&amp;gt;&lt;/code&gt; gets a calculated line height &lt;code class=&quot;highlighter-rouge&quot;&gt;8px&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;live-demonstration&quot;&gt;Live Demonstration&lt;/h2&gt;

&lt;p class=&quot;codepen&quot; data-embed-version=&quot;2&quot; data-theme-id=&quot;dark&quot; data-rerun-position=&quot;hidden&quot; data-slug-hash=&quot;JZgmwa&quot; data-height=&quot;500&quot;&gt;
  See the Pen &lt;a href=&quot;//codepen.io/pen/JZgmwa&quot;&gt;Unitless line-height demo&lt;/a&gt;
  by Joey Hoer (&lt;a href=&quot;//codepen.io/joeyhoer&quot;&gt;joeyhoer&lt;/a&gt;).
&lt;/p&gt;

&lt;p&gt;This makes code cleaner, as it takes less CSS to achieve consistent styling. Additionally, readability is improved in the same way that &lt;code class=&quot;highlighter-rouge&quot;&gt;em&lt;/code&gt;s improves readability of the &lt;code class=&quot;highlighter-rouge&quot;&gt;font-size&lt;/code&gt; property; a developer doesn’t need to know anything about the child elements to modify their relative line-heights by making effective use of the cascade.&lt;/p&gt;
</content>
 </entry>
 

</feed>
