Optimizing web content for Reader Modes and Reading Apps

Sara Soueidan, thinking about the different ways we consume web content:

The more I consume content in reading apps, the more I am reminded of the importance and the power of progressive enhancement as a strategy to create resilient and malleable experiences that work for everyone, regardless of how they choose to consume our content. The more we think about the Web in layers, the more robust experiences we can design.

The perfect example which Sara covers are the beautiful content dividers on her blog. They’re simply <hr /> elements, spiced up with CSS.

To hide content which not essential to the article in Reader Mode, Sara — again — turns to HTML by tapping into the hidden attribute.

Ah, Progressive Enhancement at its finest 🤩

Tips for optimizing content for Reader modes and reading apps →

HTML and CSS techniques to reduce your JavaScript

Anthony Ricaud, writing for the Web Performance Calendar, on the extra load that JavaScript can put on your site, and how you can replace some things with basic HTML and CSS:

Relying on solutions provided natively by browsers enables you to benefit at low cost from the expertise of the community creating web standards. These solutions generally have the advantage of using less code, thus reducing maintenance efforts for a development team (for example, no need to update the libraries used).

In this article, we will explore some of these native solutions that are available to the majority of your users.

It’s things like smooth scrolling, sticky items, carousels, etc. — those all can be implemented using pure CSS nowadays.

HTML and CSS techniques to reduce your JavaScript →

Turbo: The speed of a single-page web application without having to write any JavaScript

From the folks at Basecamp comes Turbo:

Turbo accelerates links and form submissions without requiring you to change your server-side generated HTML. It lets you carve up a page into independent frames, which can be lazy-loaded and operate as independent components. And finally, helps you make partial page updates using just HTML and a set of CRUD-like container tags. These three techniques reduce the amount of custom JavaScript that many web applications need to write by an order of magnitude.

Nice, but that “without requiring you to change your server-side generated HTML” part is not exactly true though, as you have to sprinkle some turbo-* custom elements and data-turbo-* attributes all over your markup. Once those are in place Turbo will do its thing and progressively enhance the experience.

If you want to do more than the features provided by Turbo you can bundle it up with Stimulus and Strada. All three together are published under the name Hotwire.

Turbo →

HTML Forms: How (and Why) to Prevent Double Form Submissions

When double clicking a submit button your form will be sent twice. Using JavaScript you can prevent this from happening, but wouldn’t it be nice if this behavior could be tweaked by use of an attribute on the <form>? If you think so, feel free to give this issue a thumbs up.

Today Sebastian wondered:

I quickly chimed in saying that I do tend to lock up forms when submitting them. Let me explain why …


I started locking up submitted forms as users of the apps I’m building reported that sometimes the actions they had taken — such as adding an entry — were performed twice. I took me some time to figure out what exactly was going on, but eventually I found out this was because they were double clicking the submit button of the forms. As they double clicked the button, the form got sent over twice. By locking up forms after their first submission, all successive submissions — such as those triggered by that second click of a double click — are ignored.


To prevent these extra form submissions from being made I don’t hijack the form with Ajax nor do I perform any other complicated things. I let the inner workings of the web and forms in the browser be: when pressing the submit button the browser will still collect all form data, build a new HTTP request, and execute that request.

What I simply do is extend the form’s capabilities by adding a flag — by means of a CSS class — onto the form to indicate whether it’s being submitted or not. I can then use this flag’s presence to deny any successive submissions, and also hook some CSS styles on. — Progressive Enhancement, Yay! 🎉

The code looks as follows:

 // Prevent Double Submits
document.querySelectorAll('form').forEach(form => {
	form.addEventListener('submit', (e) => {
		// Prevent if already submitting
		if (form.classList.contains('is-submitting')) {
		// Add class to hook our visual indicator on

💡 Although the problem initially was a double click problem, we don’t listen for any clicks on the submit button but listen for the form’s submit event. This way our code not only works when clicking any of the submit buttons, but also when pressing enter to submit.

With that .is-submitting class in place, we can then attach some extra CSS onto the form to give the user visual feedback. Here’s a few examples:

See the Pen
Prevent Form Double Submits
by Bramus (@bramus)
on CodePen.

See the Pen
Prevent Form Double Submits (Alternative version)
by Bramus (@bramus)
on CodePen.

Note that this solution might not cover 100% of all possible scenarios, as it doesn’t take failing networks and other things that might go wrong into account. However, as I’m relying on the basic mechanisms of the web I think I can also rely on the browser to show that typical “Confirm Form Resubmission” interstitial should a timeout occur. Additionally, if need be, one could always unlock the form after a fixed amount of time. That way the user will be able to re-submit it again.


Dealing with double form submissions isn’t a new issue at all. You’ll find quite some results when running a few queries through Google — something I only found out after stumbling over the issue myself.

Back in 2015 (!) Mattias Geniar also pondered about this, after being pulled into the subject from a sysadmin view. Now, when even sysadmins are talking about an HTML/UX issue you know there’s something going on. This made me wonder why browsers behaved like that and how we could solve it:

As a result I decided to open an issue at the WHATWG HTML Standard repo, suggesting for a way to fix this at spec level:

An attribute on <form> to tweak this behavior – instead of having to rely on JavaScript – would come in handy and form a nice addition to the spec.

I see two options to go forward:

  1. Browsers/the standard keeps the current behavior and allow multiple submits. Developers must opt-in to prevent multiple submissions using a preventmultiplesubmits attribute.
  2. Browsers/the standard adjust the current behavior to only submit forms once. Developers must opt-in to allow multiple submissions using a allowmultiplesubmits attribute.

Initial response on the issue was very low, and it looks like this isn’t that big of a priority.

Back then I was more in favor of the second solution, but now I’m quite undecided as changing the default behavior — which has been around for ages — is quite tricky.


Another way that this issue could be fixed is at the browser level: if they were to treat double clicks on form submit buttons as single clicks, then the double click problem — but not the double submit problem — could also be taken care of.

To then attach styles to forms being submitted a CSS Pseudo Class :submitting would come in handy. Come to think of it, this Pseudo Class would be a quite nice addition to CSS in itself, no matter whether this double click issue gets solved or not.


Winging back to the addition to the HTML spec I suggested: If you do think it could be something the HTML spec could benefit from, feel free to add a thumbs up to the issue to indicate that you want this, or add an extra comment to it if you have more input on the subject.

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Second-guessing the modern web

An article that’s been making rounds on Twitter today is Second-guessing the modern web:

There is a sweet spot of React: in moderately interactive interfaces. Complex forms that require immediate feedback, UIs that need to move around and react instantly. That’s where it excels.

But there’s a lot on either side of that sweet spot.

The low performance parts don’t need to be React. Listing pages, static pages, blogs – these things are increasingly built in React, but the benefits they accrue are extremely narrow. A lot of the optimizations that are cropping up in these corners, things like bundle splitting, server-side rendering, and prerendering, are triangulating, essentially, what we had before the rise of React.

If you’re swinging the React hammer, everything looks like a <Nail />. A smart developer knows when to use it, and when not to use it.

But what if you didn’t have to choose? As Dan Abramov from the React team tweeted (be sure to read the whole thread):

This proposed approach goes beyond Zeit’s Vercel’s Next.js file based routing and hits more close to Inertia.js.

Second-guessing the modern web →

N26 and the lack of JavaScript

Hugo Giraudel, engineer at N26:

In the last few years, we have seen more and more ways to build highly interactive web applications relying almost exclusively on JavaScript. To the point where we almost wonder whether we forgot from where we come from. Not so long ago was a time was JavaScript was just sprinkled on top of web pages to have custom cursors and cool sound hover effects. But I digress.

That doesn’t mean they’ve totally barred JavaScript. On the contrary: the N26 platform is built using React. What they do instead is render the React tree on the server, and make sure that it works as a regular page would. Only if the JavaScript on the client kicks in does it become a Single Page Application.

N26 and lack of JavaScript →

Smaller HTML Payloads with Service Workers

Philip Walton on how to progressively enhance your site by leveraging Service Workers to fetch partial HTML content and replace it in the DOM:

On this site, after a user visits once and the service worker is installed, that user will never request a full HTML page again. Instead the service worker will intercept requests for pages and just request the contents of those pages—everything inside the <main> element—and then the service worker will combine that content with the rest of the HTML, which is already in the cache.

By only requesting the contents of a page, the networks payloads become substantially smaller, and the pages can load quite a bit faster. For example, on this site over the past 30 days, page loads from a service worker had a 47.6% smaller network payloads, and a median First Contentful Paint (FCP) that was 52.3% faster than page loads without a service worker (416ms vs. 851ms).

The code is implemented using Workbox:

import {cacheNames} from 'workbox-core';
import {getCacheKeyForURL} from 'workbox-precaching';
import {registerRoute} from 'workbox-routing';
import {CacheFirst, StaleWhileRevalidate} from 'workbox-strategies';
import {strategy as composeStrategies} from 'workbox-streams';

const shellStrategy = new CacheFirst({cacheName: cacheNames.precache});
const contentStrategy = new StaleWhileRevalidate({cacheName: 'content'});

const navigationHandler = composeStrategies([
  () => shellStrategy.handle({
    request: new Request(getCacheKeyForURL('/shell-start.html')),
  ({url}) => contentStrategy.handle({
    request: new Request(url.pathname + 'index.content.html'),
  () => shellStrategy.handle({
    request: new Request(getCacheKeyForURL('/shell-end.html')),

registerRoute(({request}) => request.mode === 'navigate', navigationHandler);

Smaller HTML Payloads with Service Workers →

🔗 Related: Unpoly, which uses HTML attributes to let you control partial rerenders using XHR fetches.

Move Fast & Don’t Break Things

Embedded above, but also available as a transcript, is Scott Jehl’s talk “Move Fast & Don’t Break Things” on how to keep your website performant.

The talk starts of with Progressive Enhancement (of course!) and covers things such as Responsive Images, Native Lazy Loading of Images, font-display: swap;, Paint Metrics, etc.

Move Fast & Don’t Break Things (transcript)

Unpoly – The unobtrusive JavaScript framework for server-side web applications

Unpoly is an unobtrusive Javascript framework for applications that render on the server. It allows your views to do things that are not normally possible in HTML, such as having links update only fragments of a page, or opening links in modal dialogs.

Unpoly can give your server-side application fast and flexible frontends that feel like a single-page application (SPA). It also preserves the resilience and simplicity of the server-side programming model

Ooh, I like this a lot. Using some HTML attributes you augment your typical HTML+CSS website and have (parts of) new pages load up in modals, have them slide in, do partial updates, etc.

Here’s a modal for example:

<!-- preview.html -->
<a href="full.html" up-modal=".story">Read full story</a>

<!-- full.html -->
<!doctype html>
    <div class="story">
      <h2>Full story</h2>
      <p>Lorem ipsum dolor sit amet.</p>

Unpoly will load the entire page full.html using Ajax, and then only show the contents of its .story inside the modal. The rest will be discarded.

That way your website will still work on browsers with JS disabled – including the very first webbrowser – and provide a richer experience to those who support have the latest and greatest browsers running.


Unpoly →