Accelerated Mobile Pages (AMP)

One of the advantages of having a blog, especially one built using a static site generator like Jekyll, is it’s extremely easy to craft the HTML and have it compile as you’d expect it too. No CMS rendering out a load of HTML / CSS / JS that most likely isn’t required and just bloats the page. Having such a site also allows me to experiment with technology that would require quite a major refactor on a larger client build. Recently I have been looking at Accelerated Mobile Pages (AMP). I have implemented the changes required to support it across the blog… did you notice?

What is AMP?

Accelerated Mobile Pages is an open source project that aims to improve a user experience while browsing web content on a mobile device. It does this by taking standard HTML, and limiting its usage to a specific set of functionality that is clearly defined in the AMP spec documentation. The reason for this limitation is to improve performance on mobile devices, by only allowing current best practices across a wide range of component patterns. So for example, there are strict rules around the use of script and style tags. Too many of each could potentially slow down a page on a mobile device by blocking page rendering. AMP comes with its own validator that will report these errors so they can be corrected.

AMP comes in three parts:

  • AMP HTML
  • AMP JS
  • Google AMP Cache

AMP HTML is much like regular HTML, only extended slightly with a set of custom AMP properties. Some regular HTML tags have been replaced with AMP specific tags, allowing for common development patterns to be easily implemented in a performant manor. See the AMP HTML Spec for more details.

AMP JS is the glue that binds everything together. It is a library that adds all the custom elements mentioned above, and implements all of the AMP best practices. A key feature of the library includes allowing only asynchronous scripts, so nothing can block the DOM. Another large change (for me anyway) was all resources must be sized statically, meaning all images must have a width and height attribute. Including this width and height attribute allows the browser to render a page much quicker and with less need for redrawing as the DOM is constructed.

I have yet to see the Google AMP Cache in action, but now that the site is live with the tweaked AMP HTML and JS, I will monitor Google Webmaster Tools for AMP errors so Google can start caching the pages. From what I can tell this is an automated process that happens when the pages are crawled, and not something a developer has direct control over (other than to fix errors).

So what did I change?

Most of the changes I needed to make were fairly minor. These included:

  • Adding CSS boilerplate code for AMP
  • Inlining current CSS in amp-custom style tag
  • Including required AMP JS library. This also included the form, service worker, and analytics extension scripts
  • Fixing the markup that was output by the Rouge code highlighter

There were a couple of changes that required a lot more work:

  • Converting standard img tags into amp-img tags
  • Making sure all images had a width and height attributes

The use of amp-img gives a few performance benefits including lazy-loading and srcset support (polyfilled if not native to the browser). AMP-img requires a width and height attribute to be set so it can calculate the correct aspect-ratio of the image. In my case this was very time consuming task, as I had to manually add the width and height attributes for each image used across the blog. If I were to be using a standard CMS, I’d be able to add these attributes very quickly then let the server do all the work!

An example of a basic AMP HTML page is given below. As you can see it is all very standard apart from a couple of custom components from AMP. For more information on the AMP HTML setup see here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!doctype html>
<html amp lang="en">
  <head>
    <meta charset="utf-8">
    <title>Hello, AMPs</title>
    <link rel="canonical" href="http://example.ampproject.org/article-metadata.html" />
    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
    <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
    <style amp-custom>/* Custom CSS goes here */</style>
    <!-- AMP analytics script added here if required -->
    <script async src="https://cdn.ampproject.org/v0.js"></script>
    <!-- Any additional AMP scripts added here -->
  </head>
  <body>
    <h1>Welcome to the mobile web</h1>
  </body>
</html>

Here are a few links I found useful, and are a good starting point if you wish to implement AMP on your own site.

Useful image size shell script

As I mentioned earlier in the post I use Jekyll as my static site generator. Because of this I had to add all the height and width attributes manually. Not a simple task when there are over 400 images that need fixing. To speed up the task I put together a very simple shell script. It loops through any images in a given directory, and outputs the width and height of each in the correct format for my Jekyll setup.

1
2
3
4
5
6
#!/bin/bash
shopt -s nullglob
for f in *.jpg *.png *.gif
do
	identify $f | awk '{print $1" "$3}' | awk '{print $1; split($2,a,"x"); print "width: "a[1]"\nheight: "a[2]"\n"}'
done

An example of the output of the script to the console is given below. This can easily be copy / pasted into the relevant markdown file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
01-initial.gif
width: 767
height: 764

02-rigid.gif
width: 766
height: 764

03-large-panel.gif
width: 766
height: 764

featured.gif
width: 600
height: 250

Fixing Jekyll Rouge

AMP is very picky about the structure of your HTML. If your pages don’t validate then they won’t be cached by Google, which is what you want for optimum performance on mobile. The main problem I ran into here was Jekyll Rouge was creating HTML that wasn’t AMP valid. A couple of inline style attributes on a table element were enough to invalidate the page.

Looking at the Rouge GitHub page I could see there was already an open issue; and it isn’t going to be fixed anytime soon. Luckily there is a way using the Liquid template engine to modify the HTML output before final compilation:

1
2
3
4
{ % if _code contains '<pre class="lineno">' % }
    { % assign _code = _code | replace: '<table style="border-spacing: 0">', '<table>' % }
    { % assign _code = _code | replace: '<td class="gutter gl" style="text-align: right">', '<td class="gutter gl">' % }
{ % endif % }

The above code looks for the pre class="lineno" element, and does a search and replace on the HTML where required. To use, wrap your Liquid highlight include like so:

1
2
{ % capture _code % }{ % highlight plaintext linenos=table % }
{ % endhighlight % }{ % endcapture % }{ % include fixlinenos.html % }{ { _code } }

And there you have it, valid AMP HTML coming from Rouge. Just remember to add the required CSS back into your standard stylesheet.

Note: I have added spaces around the liquid tags to stop Jekyll getting confused when compiling. Remove these spaces when you add them to your build.

Final thoughts

AMP is fairly simple to implement, although it does come with a strict set of functional restrictions which you will need to work with for your pages to validate. There’s an extensive range of components that can be used today, with more being developed and tested all the time. Time will tell on whether the extra development effort is worth it in terms of performance and usability. I’ve defiantly noticed a speed increase of the site on a mobile when loading over a 3G connection (although I haven’t timed it to be 100% sure it isn’t a placebo). I will be monitoring my analytics data closely over the coming weeks, to see if there’s any data that can support a case for overall improvement. But is it the future of the mobile Web? Ask me again in 5 years and I’ll tell you! It is certainly an interesting approach that you should consider investigating if you want to increase your mobile user base.