Jonty Newman

Stop Writing JavaScript*

* unless your application is treating the Web as a runtime environment.

In the early 2010s, the principle of progressive enhancement was beginning to trend. As the end of the decade approaches, progressive enhancement is arguably as important today as it has ever been.

Web developers should aspire towards reducing JavaScript or even removing it entirely, despite the fact that it is an anomaly for a modern browser to not support JavaScript.

To be clear, this article is referring to client-side JavaScript that is executed in a web browser, and not JavaScript as used in other technologies such as Node.js.

Why stop?

An argument could be made that we live in a time when one can assume that JavaScript is part of the web platform, and hence that progressive enhancement is dead. However, a prevalent technology is not necessarily one that should be relied upon. In fact, there still exist institutions that reserve JavaScript for enhancing functionality rather than implementing it outright.

Examples of these institutions would be British public sector websites such as the NHS website and GOV.UK, the latter of which gives the following reasons for its stance:

...a system shouldn’t break wholly if a single part of it fails or isn’t supported.’s often not the conscious choice of users to arrive at your site without JavaScript.

Some users turn off features in their browsers deliberately.

In summary, the motives of progressive enhancement are centred around resilience and inclusiveness (as stated by former GOV.UK technical architect Brad Wright).


Progressive enhancement is the act of building web applications as incremental layers of HTML, CSS, and JavaScript. Therefore, citing progressive enhancement alone is not sufficient justification for removing JavaScript entirely. However, the fundamental difference of JavaScript when compared to both HTML and CSS is that it is an imperative programming language, while HTML and CSS are declarative programming languages.

When a developer implements an application using an imperative language such as JavaScript, they must concern themselves with state and the flow of control. This leads to increased complexity, and hence a greater requirement for maintenance. In contrast, an application that has been implemented using a declarative language offloads a great deal of that burden to its processor.


It is more than possible to build interactive experiences with HTML and CSS alone. This can be achieved via animations as well as pseudo-classes such as :target.

The following is an example of an interactive image gallery implemented as pure HTML and CSS.


<article class="gallery">
  <section class="gallery--viewer">
    <img  id="circle-red"
         alt="A red circle."
    <img  id="triangle-green"
         alt="A green triangle."
    <img  id="square-blue"
         alt="A blue square."
  <ul class="gallery--icons">
      <a href="#circle-red">
        A red circle
      <a href="#triangle-green">
        A green triangle
      <a href="#square-blue">
        A blue square


.gallery > .gallery--viewer,
.gallery > .gallery--viewer > img {
  height: 128px;

.gallery > .gallery--viewer > img {
  display: none;
  margin-left: auto;
  margin-right: auto;
  padding-top: 1rem;
  padding-bottom: 1rem;

.gallery > .gallery--viewer > img:target {
  display: block;



It may not be possible to implement every desired behaviour with HTML and CSS alone. For example, we may not want the scroll offset to seem like it's jumping to the gallery when an image is selected. The only feasible way to achieve this is through JavaScript.

var selector = {
  icons: '.gallery > > li > a',
  images: '.gallery > > img'

var icons = document.querySelectorAll(selector.icons);
var images = document.querySelectorAll(selector.images);

var listener = function (event) {

  var href ='href');
  var target = document.querySelector(href);
  for (var i = 0; i < images.length; i++) {
    images[i].setAttribute('style', 'display: none;');
  target.setAttribute('style', 'display: block;');

for (var i = 0; i < icons.length; i++) {
  icons[i].addEventListener('click', listener);

Although this would achieve progressive enhancement, its greatest tragedy is that it reimplements the behaviour that was previously declared using CSS. The developer of the application would need to determine if the gained functionality is worth the additional maintenance.


To avoid scenarios in which the same functionality is implemented using different technologies, a developer can decide whether they are distributing a document or an application.

In other words, the developer can decide whether the Web is being relied upon as either a document viewer or a runtime environment. For the former, solely HTML and CSS can be leveraged. For the latter, technologies such as JavaScript and WebAssembly can be used outright.

There is a cost incurred with either of these approaches. A document will have limited functionality, while an outright JavaScript or WebAssembly application will potentially have less resilience and reduced inclusivity. If a compromise is needed between these two choices, then progressive enhancement may be viable.


JavaScript is a double-edged sword. Although it allows for much greater flexibility, it comes at the cost of complexity and at the expense of maintainability, resilience and inclusivity.

A web developer should therefore aspire towards no JavaScript at all, and instead serve a document using HTML and CSS alone. If enhancements are necessary, then these should be carefully considered and implemented progressively.