Progressive enhancement

How and why is should be used

“When a lift fails it is useless. When an escalator fails it becomes stairs” — we should be building escalators and not lifts.

This is not just about users with JavaScript turned off (and also not just about JS, but HTML and CSS too), remember that all your users are non-JS users whilst your JS is loading. With the diversity of user agents out there, JS failing isn’t an edge case. Sometimes it will be the browsers fault and sometimes it will be yours.

The object is not to make sure everything works in all situations. Your core journey should be functional in all situations but optimised for most.

Serve something basic and useful to everyone. Heavily qualify your enhancements from there and put your effort into this.

Using a JavaScript only solution is placing developer convenience first at the expense of user convenience

Reduce test effort

When working on a server you have a (mostly) known environment eg. Django 1.7 running on Python 2.7.3 etc. This makes testing relatively straightforward. On the client side you do not control the execution environment and code must be tested on a diverse range of devices and versions of browsers.

Performance

With the increase in mobile usage there is both an increase in connection speed variability and the uncertainty around it. The time to first render is very important and with PE this does not depend on the JS having loaded. Both Twitter and AirBnB rebuilt their services using PE to improve their performance.

"Doubling up on work"

There is a misconception that PE doubles up on work by building on both the server and client side. But if you lean on the server as much as possible and use a lean client side you largely avoid this.

Content

On the web you must put content at the forefront (content first). Prioritise getting it down the wire as fast as possible.

Work with the browser

We should always work with the browser and not against it. The more behaviour you take over and replace via JS the more complex expected behaviour you have to re-implement. Not all of this functionality is consistent across browsers and so you can not consistently re-implement this for all users. The more JS you add to support older browsers degrades the performance on newer ones also.

Future-friendly

It's about supporting future devices more than past devices. Shifts your effort to newer devices where it belongs.

Not about catering for users with JS off

It's about catering for poor connections, slow devices, the unexpected.

SEO

Google 27/10/14 - "Make sure you adhere to the principles of progressive enhancement"


Development examples

Show/hide

One of the simplest implementations of progressive enhancement can be demonstrated in how to build a show/hide section. When looking at the problem, start with breaking it down to its most simple form, in this case it would be a heading with a collection of content. To then achieve the end goal JavaScript can be used to manipulate the markup and add in the required functionality. In this case the JavaScript makes the headings tabbable/clickable items that can show/hide the content directly below them.

Prior to JavaScript running / non-JavaScript

Mauris tristique posuere erat

Nunc et velit tincidunt, consequat quam vitae, tincidunt risus. Mauris consequat fringilla dictum. Curabitur faucibus vitae nisl sed euismod. Curabitur nec porttitor quam, at finibus est. Vivamus quis consequat tortor, sed mollis velit. Quisque ornare dictum consectetur. Pellentesque dictum lacus sed tincidunt sollicitudin. Sed nec bibendum nisi, a tempor nisl. Etiam eget posuere nulla.

Aenean non metus nibh. Pellentesque ullamcorper nibh vel luctus sodales. Integer in tincidunt velit. Donec sed posuere nibh, quis rhoncus sapien. Nam vitae dolor eget velit sodales porta. Donec maximus placerat erat et vehicula. Etiam leo neque, posuere et efficitur ut, convallis nec odio. Nullam a tincidunt nisi. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent varius enim ac porttitor laoreet. Proin scelerisque suscipit dui. Vestibulum odio magna, feugiat eget metus nec, elementum tincidunt sapien. Nam vitae elementum orci.

Nulla facilisi. Sed diam sem, faucibus a ex et, aliquet sagittis nulla. Curabitur interdum vel turpis quis volutpat. Maecenas tempus non lectus a pretium. Pellentesque eget risus vehicula, interdum neque at, malesuada diam. Donec non pellentesque lorem. Donec hendrerit enim risus, quis sollicitudin augue porta nec. Fusce lobortis neque vitae mattis commodo. In vestibulum tincidunt purus, vitae lacinia magna fringilla facilisis. Maecenas malesuada vehicula quam, sit amet faucibus est elementum id. Cras iaculis enim purus, eleifend porta magna sollicitudin sit amet. Aenean a dui lorem. Praesent consequat ligula aliquet, posuere metus nec, placerat nisl. Vestibulum dictum commodo rhoncus. Ut molestie ultrices augue.

Sed dignissim metus eget neque euismod

Proin in scelerisque leo, non cursus erat. Cras vel nibh malesuada, pharetra eros et, vestibulum lorem. Maecenas lobortis at nulla eget euismod. Quisque sit amet lacus id nibh placerat finibus. Nunc suscipit est ut feugiat rutrum.

<div class="m-showhide" data-module="m-showhide">
    <h2 class="heading heading--2 a-heading--light m-showhide__heading">...</h2>
    <div class="m-showhide__content">
        ...
    </div>
    <h2 class="a-heading a-heading--2 a-heading--light showhide__heading">...</h2>
    <h2 class="a-heading a-heading--2 a-heading--light m-showhide__heading">...</h2>
    <div class="m-showhide__content">
        ...
    </div>
</div>

Post JavaScript running

Mauris tristique posuere erat

Nunc et velit tincidunt, consequat quam vitae, tincidunt risus. Mauris consequat fringilla dictum. Curabitur faucibus vitae nisl sed euismod. Curabitur nec porttitor quam, at finibus est. Vivamus quis consequat tortor, sed mollis velit. Quisque ornare dictum consectetur. Pellentesque dictum lacus sed tincidunt sollicitudin. Sed nec bibendum nisi, a tempor nisl. Etiam eget posuere nulla.

Aenean non metus nibh. Pellentesque ullamcorper nibh vel luctus sodales. Integer in tincidunt velit. Donec sed posuere nibh, quis rhoncus sapien. Nam vitae dolor eget velit sodales porta. Donec maximus placerat erat et vehicula. Etiam leo neque, posuere et efficitur ut, convallis nec odio. Nullam a tincidunt nisi. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent varius enim ac porttitor laoreet. Proin scelerisque suscipit dui. Vestibulum odio magna, feugiat eget metus nec, elementum tincidunt sapien. Nam vitae elementum orci.

Nulla facilisi. Sed diam sem, faucibus a ex et, aliquet sagittis nulla. Curabitur interdum vel turpis quis volutpat. Maecenas tempus non lectus a pretium. Pellentesque eget risus vehicula, interdum neque at, malesuada diam. Donec non pellentesque lorem. Donec hendrerit enim risus, quis sollicitudin augue porta nec. Fusce lobortis neque vitae mattis commodo. In vestibulum tincidunt purus, vitae lacinia magna fringilla facilisis. Maecenas malesuada vehicula quam, sit amet faucibus est elementum id. Cras iaculis enim purus, eleifend porta magna sollicitudin sit amet. Aenean a dui lorem. Praesent consequat ligula aliquet, posuere metus nec, placerat nisl. Vestibulum dictum commodo rhoncus. Ut molestie ultrices augue.

Sed dignissim metus eget neque euismod

Proin in scelerisque leo, non cursus erat. Cras vel nibh malesuada, pharetra eros et, vestibulum lorem. Maecenas lobortis at nulla eget euismod. Quisque sit amet lacus id nibh placerat finibus. Nunc suscipit est ut feugiat rutrum.

<div class="m-showhide js-m-showhide" data-module="m-showhide">
    <h5 class="a-heading a-heading--2 a-heading--light m-showhide__heading"><a class="m-showhide__control" href="#term2" aria-controls="#term2" aria-expanded="false">...</a></h5>
    <div class="m-showhide__content" style="display: none;" id="term2" aria-hidden="true">
        ...
    </div>
    <h5 class="a-heading a-heading--2 a-heading--light m-showhide__heading"><a class="m-showhide__control" href="#term3" aria-controls="#term3" aria-expanded="false">...</a></h5>
    <div class="m-showhide__content" style="display: none;" id="term3" aria-hidden="true">
        ...
    </div>
</div>

Modal window

When looking at modal windows/overlays there are at least a couple of different scenarios/solutions available. The first is content that could be stored within the current page and linked to if not displayed in the modal, the second is content that would be on a different page but needs to be displayed without taking the user away from the page they are currently on if at all possible.

Content within the page

Place the content in a position on the page where it can be displayed sensibly and use an in-page anchor to link down to the content. This provides the fallback to take the user to the content if JavaScript does not run. When JavaScript is available hide the content for the modal window and place this in an appropriately styled overlay when the link is selected rather than scrolling the user to the content.

Prior to JavaScript running / non-JavaScript

Modal example 1

Travel insurance

As an existing customer get 15% off when you buy a new Annual Travel policy

Post JavaScript running

Modal example 2

Travel insurance

As an existing customer get 15% off when you buy a new Annual Travel policy

Content from a different page

Place the content in a targetable area (possibly using an id) and link page. If JavaScript is not available the user will simply be taken to the page to see the content, when it is the content can be loaded in using AJAX and placed within an appropriately styled overlay.

Prior to JavaScript running / non-JavaScript
Post JavaScript running

Multi-step quote journey

A more complex example would be a multi-step quote journey with functionality such as postcode lookups, inline form validation, dynamic question sets and keeping the user on the same page.

Prior to JavaScript running / non-JavaScript

Postcode lookup

There are a couple of solutions that could be implemented:

  • Fields to enter the complete address are available to be filled in. No postcode look up functionality offered.
  • When a user submits their postcode the page is refreshed and then contains the results of the postcode lookup.
Inline form validation

All validation done server-side on form submission.

Dynamic question sets

There are a couple of solutions that could be implemented:

  • Implement with the different question sets displayed on separate pages with each form section submitted and next set displayed on page refresh (if no errors found)
  • Display all the questions on the page with clear instructions around which questions are required to be filled in each scenario (just as would be done with a printed form)
Keeping the user on the same page

There are a couple of solutions that could be implemented:

  • Keep all form fields on a single page
  • Split over multiple pages and refresh the page

Post JavaScript running

Postcode lookup

Use AJAX to retrieve postcode lookup results and display appropriately to the user without causing the page to refresh.

Inline form validation

As the user completes each field use JavaScript to validate the fields entry and instantly announce errors to the user so that they can correct errors before the form is submitted. Server side validation must still be done on final submission.

Dynamic question sets

There are a couple of solutions that could be implemented:

  • Contain the questions on the same page and use JavaScript to hide the extra questions until particular question has been answered then show the appropriate form fields
  • On answering a particular question AJAX in the new question set
Keeping the user on the same page

Submit each from page via JavaScript and display the next set of questions/results using the response back from the server.