Speedy mobile web pages with AMP

Accelerated Mobile Pages (AMP) is a technique for making fast rendering web pages on mobile devices. It is primarily intended for static and non-interactive content, such as articles.

AMP is written in AMP HTML, which is HTML with certain restrictions and additions. For example, custom JavaScript is banned, img tags are replaced with amp-img tags and all styling must be inside the head tag.

AMP HTML is interpreted partly by the web browser and partly by AMP JS. The latter is a JavaScript library that takes care of resource loading, layout calculations and implements custom tags. For example, for the amp-img tag AMP JS will:

  1. find the amp-img tag
  2. reserve space for the image in the layout
  3. load the image asynchronously
  4. add the image to the DOM

The great thing about AMP is that it is built entirely upon HTML standards and JavaScript. Therefore, AMP works natively in any web browser.

For a practical example, take a look at this ordinary static web page.

<!DOCTYPE html>
<html>
<head>

<title>Berries</title>
<link type="text/css" rel="stylesheet" href="style.css" />

</head>
<body>

<article>
    <img alt="Blueberries" src="Blueberries.jpg" />
    <h1>Forest food</h1>
    <p>
        The word berry is used for many different kinds of small fruits...
    </p>
</article>

</body>
</html>

Then check out this AMP version of the same page.

<!doctype html>
<html amp>
<head>

<meta charset="utf-8">
<script async src="https://cdn.ampproject.org/v0.js"></script>
<title>Accelerated berries</title>
<link rel="canonical" href="index.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>
    body{font-family: arial; font-size: 14px; background: maroon;}
    article{width: 700px; margin: 50px auto; background: white; padding: 0 0 15px 0;}
    h1{font-size: 1.5em; margin: 10px 20px;}
    p{margin: 5px 20px 5px;}
</style>

</head>
<body>

<article>
    <amp-img alt="Berries" src="Blueberries.jpg" height="500" width="700"></amp-img>
    <h1>Forest food</h1>
    <p>
        The word berry is used for many different kinds of small fruits...
    </p>
</article>

</body>
</html>

Note: The code shown above will not pass AMP validation because the validator is very pedantic about whitespace in the the amp-boilerplate style. I wanted it to be readable here. The code in the linked AMP page validates properly.

Advertisements

Making a user friendly web form

Here I have tried to put together a form that is simple, practical and adheres to user accessible guide lines.

A user friendly web form

Example of an accessible form field

<div class="field">
    <label for="temp">Title here <span class="required-star"></span></label>
    <input id="temp" class="js-required" type="text" name="temp"
            aria-required="true" placeholder="e.g. example here" />
    <span class="error js-required-error" style="display: none" role="alert">
        Error message here
    </span>
    <span class="note">
        Extra information here
    </span>
</div>
  • Red stars for required fields is a common convention
  • Examples as placeholders may occasionally be useful, but make sure they are understood as examples
  • The aria-required and role attributes are there to help screen readers
  • Error messages should appear when the user leaves the input (i.e. on blur), that way they appear as early as possible without being disruptive during typing
  • If there are errors when the user submits the form an error message should appear at the top in addition to the inline errors
  • Prefer radio buttons over select boxes when there are few options

Data in a web page

There are several places to store data in an ASP.NET web page. These places are far from interchangeable. Careless placement of data can lead to a poor web page and bad user experience. The following are some guide lines that I consider appropriate.

URL

Use for: Data that defines the web page. Such as the ID for a product or the terms for search results.

Remember: This becomes permanent if the page is bookmarked. Don’t store temporary modifiers in the URL. For example, if the user bookmarks the login page after a failed login attempt they shouldn’t have to see the error message every time they return.

DOM

Use for: Data that makes up the web page. Apart from the obvious, text and HTML, this can include JSON to be used by JavaScript.

Remember: Data attributes are a good place to store data for use in JavaScript.

Global JavaScript variables

Use for: Keeping state in single page applications or other long lived web pages with lots of JavaScript.

Remember: In JavaScript all scripts share the same namespace. Take care that your variables don’t clash with other scripts, for example by putting all your global variables into one global object. There are other ways to handle this as well.

Local storage

Use for: Applications where you can’t or don’t want to store data server side.

Remember: This data is stored permanently in the browser. The data won’t be available if the user returns with a different browser or device.

Cookie

Use for: Remembering users across visits on web sites requiring login. Or remembering user settings on web sites that does not use logins.

Remember: Current EU law dictates that web sites targeting EU citizens must obtain user permission when setting cookies. Though there are some exceptions.

Session

Use for: Data related to the currently logged in user.

Remember: The session is controlled by a session cookie stored in the browser. ASP.NET takes care of this automatically. It is the browser that determines when the session ends by removing the cookie. Session cookies used for authentication purposes does not fall under the EU law described above.

TempData

Use for: Data you want to keep between page requests, but not longer. Good for showing the user that data has been saved for example.

Remember: This data is stored in the session but has the extra property that it disappears after being read.

Application data

Use for: As a cache for common data needed server side.

Remember: This data is common for all users and persists until the application reloads. The data must be handled in a thread safe manner.

Database

Use for: Everything that needs permanent storage.

Remember: Database design and management is a whole science in and of itself.

Web components of HTML

Web Components consists of four separate standards. Together they aim to facilitate the creation of reusable and self-contained pieces of HTML (with JavaScript and CSS). Browser support is lacking but can be polyfilled.

Templates

The template tag holds markup that can be used from JavaScript. It provides a browser native alternative to JavaScript templating libraries.

<div class="fact">All birds are animals</div>
<div class="fact">Some animals are dogs</div>

<template id="fact-box">
    <h1>Fact</h1>
    <p></p>
</template>

<script>
    var template = document.querySelector("#fact-box").content;
    var facts = document.querySelectorAll(".fact");
    [].forEach.call(facts, function(fact) {
        var clone = document.importNode(template, true);
        clone.querySelector("p").innerHTML = fact.innerHTML;
        fact.replaceChild(clone, fact.firstChild);
    });
</script>

Shadow DOM

The shadow DOM is a way to create nested DOMs. They can then be styled independently from the rest of the page. The content tag provides a way to enter content into the shadow DOM from the shadow root element.

<div class="fact">All birds are animals</div>
<div class="fact">Some animals are dogs</div>

<template id="fact-box">
    <h1>Fact</h1>
    <p><content></content></p>
</template>

<script>
    var template = document.querySelector("#fact-box").content;
    var facts = document.querySelectorAll(".fact");
    [].forEach.call(facts, function(fact) {
        var clone = document.importNode(template, true);
        fact.createShadowRoot().appendChild(clone);
    });
</script>

Custom elements

Creating custom elements allow us to name our pieces of HTML. To distinguish user made tags from official tags the former must always contain a hyphen.

<fact-box>All birds are animals</fact-box>
<fact-box>Some animals are dogs</fact-box>

<template id="fact-box">
    <h1>Fact</h1>
    <p><content></content></p>
</template>

<script>
    var template = document.querySelector("#fact-box").content;
    var prototype = Object.create(HTMLElement.prototype);
    prototype.createdCallback = function() {
        var clone = document.importNode(template, true);
        this.createShadowRoot().appendChild(clone);
    };
    document.registerElement("fact-box", {prototype: prototype});
</script>

HTML imports

Finally, with HTML imports, the finished web component can be placed in an HTML file and imported where it is needed.

<head>
    <link rel="import" href="fact-box.html">
</head>

<fact-box>All birds are animals</fact-box>
<fact-box>Some animals are dogs</fact-box>
<template id="fact-box">
    <h1>Fact</h1>
    <p><content></content></p>
</template>

<script>
    var thisDocument = document.currentScript.ownerDocument;
    var template = thisDocument.querySelector("#fact-box").content;
    var prototype = Object.create(HTMLElement.prototype);
    prototype.createdCallback = function() {
        var clone = document.importNode(template, true);
        this.createShadowRoot().appendChild(clone);
    };
    document.registerElement("fact-box", {prototype: prototype});
</script>

Security in headers

Cross site scripting (XSS) attacks can be difficult to prevent when developing web sites. However, the browser can be recruited in this struggle by sending it the appropriate headers. This example shows how this might be setup in the web.config file of a simple HTTPS enabled ASP.NET site.

<configuration>
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Content-Security-Policy"
          value="default-src 'none'; img-src 'self'; style-src 'self'; script-src 'self'" />
        <add name="X-Frame-Options" value="deny" />
        <add name="X-XSS-Protection" value="1; mode=block"/>
        <add name="X-Content-Type-Options" value="nosniff" />
        <add name="Strict-Transport-Security" value="max-age=31536000" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
  <system.web>
    <httpCookies httpOnlyCookies="true" requireSSL="true" />
  </system.web>
</configuration>

Content-Security-Policy tells the browser where content should be allowed from. In this example only images, style sheets and script files from the same origin as the page are allowed. Everything else is blocked, including inline scripts and styles. Other content that can be controlled are fonts, media, plugins, connections and frames. Internet Explorer lacks support for this header.

X-Frame-Options provide the reverse control over frames. While Content-Security-Policy controls frames within the page this header controls the page within frames. In other words it can stop your page from being embedded inside someone else’s website.

X-XSS-Protection controls how Internet Explorer handles expected XSS attacks. This filter is already active by default but can be told to block pages completely instead of trying to sanitize them.

X-Content-Type-Options with its only valid option nosniff tells Internet Explorer to stop guessing MIME types and instead trust the server to set the content type correctly.

Strict-Transport-Security tells the browser that this host should be accessed via HTTPS. It is ignored when returned over HTTP. The normal use case for this header is to redirect users arriving via HTTP to HTTPS and return this header to tell the browser to connect directly via HTTPS in the future. This is not supported in Internet Explorer.

The last setting in the example tells ASP.NET to send cookies with the HttpOnly and Secure attributes. Together these tell the browser to only transmit the cookie over HTTPS and not expose it to client side scripts. Take care that you only access the server via HTTPS when using requireSSL because the ASP.NET session identifier will be recreated when the session cookie is not present in the request.

Inline vector graphics

Scalable Vector Graphics (SVG) is an XML based image format for vector images. With HTML5 we can use SVG inline in HTML documents.

<svg id="flag" width="450" height="300" viewBox="0 0 18 12">
  <defs>
    <mask id="mask">
      <rect x="0" y="0" width="18" height="6" fill="#ffffff" />
    </mask>
  </defs>
  <rect x="0" y="0" width="18" height="12" fill="#ffffff" />
  <rect x="0" y="6" width="18" height="6" fill="#d00c33" />
  <circle cx="7" cy="6" r="4" fill="#ffffff" />
  <circle cx="7" cy="6" r="4" mask="url(#mask)" fill="#d00c33" />
</svg>

The svg element is used much like an img element but instead of referring to an external file we put the content directly in the document. The viewbox attribute defines the coordinate system we want to use for the SVG image. This should not be confused with the height and width attributes which determines the size of the image in the document. They should agree on the aspect ratio though.

Support for inline SVG is in most current browsers. Unfortunately, there is no good way of achieving compatibility with older browsers. Having SVG images in separate files has a bit more support but Internet Explorer 8 or older still does not support it. This can be fixed with external libraries though.

I have created an example page where you can see it in action.

Video goes native

With HTML5 we get native support for video. We no longer have to rely on third party plugins such as Flash. All current browsers have support for the video tag but unfortunately they can’t agree on what formats to support.

WebM MPEG-4 Ogg
Internet Explorer Supported
Firefox Supported Supported
Chrome Supported Supported
Safari Supported
Opera Supported Supported

This means that we have to convert our videos into two formats if we want everyone to be able to watch them. If we also want to fall back to Flash for older browsers we need to keep every video in three formats. I have created an example page where you can see the video tag in action.

<video width="160px" height="120px" controls poster="tractor.jpg">
  <source src="tractor.webm" type='video/webm; codecs="vp8, vorbis"' />
  <source src="tractor.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' />
  Video tag not supported
</video>

In this example we set up a video with two sources. We specify the dimensions of the video to help the browser out just like we do with images. The controls attribute means that the browser will show its native controls for the user to control playback, set the volume etcetera. There is also a JavaScript interface for playback that you can use if you want to create your own controls.

The poster is an image that will be used as a placeholder until playback has been started. It should of course have the same dimensions as the video. In case the browser does not support the video tag we inform the user about it with a message. If we wanted to use a Flash player as a fallback we would have put it here instead of the message.

For each source we specify the MIME type and the codecs used. This way the browser doesn’t have to download the file to determine if it can play it. You must also make sure that your web server provides the correct MIME type in the Content-Type HTTP header or it might not work properly.

Sound as well

HTML5 also have a new audio tag that works the same way. Again the browsers can’t agree on what formats to support.

Ogg MP3 Wav
Internet Explorer Supported
Firefox Supported Supported
Chrome Supported Supported Supported
Safari Supported Supported
Opera Supported Supported

I have created yet another example page where you can see the audio tag in action.

Semantic markup in HTML5

HTML5 introduces several new tags for semantic markup. The purpose is to replace the divs with home made classes and ids with something standardized. This will be helpful when the content is read by something other than a standard browser. The example below shows some of the main elements in the new system.

<body>
    <header>
      <h1>Site title</h1>
    </header>
    <nav>
        <a href="url">Link</a>
        <a href="url">Link</a>
    </nav>
    <section id="articles">
        <article>
            <h1>Heading</h1>
            <p>Paragraph text.</p>
            <h2>Sub heading</h2>
            <p>More text.</p>
            <section>
                <h1>Second sub heading</h1>
                <p>Even more text.</p>
            </section>
        </article>
        <aside>
            Advertisement
        </aside>
    </section>
    <footer>Copyright notice</footer>
</body>

The article tag is used for a self contained independent pieces, like a blog post or an article or anything that could be distributed on its own. A section is used to group related content together, for example a collection of articles or a set of paragraphs. There are also implicit sections. In the example above the article contains three sections of which only the last is explicit.

There is a lot more to say about sections in HTML5 and about all the other elements. Two things I will say though. Remember that header and headings are not the same. Secondly, you should still use divs in cases where new tags doesn’t make sense.

Browser compatibility

All modern browsers support the new elements. Most older browsers will handle the new tags the same way as spans. That means they are styled as inline by default. This can be fixed easily with a bit of CSS.

section, article, header, footer, hgroup, aside, nav {
  display: block;
}

However, this doesn’t work in Internet Explorer 8 and below which doesn’t allow you to style elements it doesn’t recognize. This can be fixed with the following workaround.

<!--[if lte IE 8]>
  <script>
    document.createElement("section");
    document.createElement("article");
    document.createElement("header");
    document.createElement("footer");
    document.createElement("hgroup");
    document.createElement("aside");
    document.createElement("nav");
  </script>
<![endif]-->

In reality there are more elements than these. You can either include them as you need or use a finished script that does this for you. Unfortunately this wont work if the user has disabled JavaScript. In that case the only thing you can do is inform them about it with a noscript tag and proceed without CSS for the new elements.