Case Studies

Case Study: Fixing Cumulative Layout Shift (CLS) with Netlify Edge Functions

Background

Finding information and ongoing support in a digital world can be challenging when many websites and apps are not optimised for accessibility. Blind Veterans UK understands this problem intimately. Since 1915, they’ve provided support to tens of thousands of vision-impaired veterans. They recently began working with us, Reason Digital, to replace their site with a high performing website for their service users and supporters with the following requirements:

Site Accessibility

Their service users need the website to be maximally accessible to get the information they need quickly and easily.

Security

Unfortunately charity websites undergo the same attacks as commercial websites so they wanted to make sure their end users had a secure web experience, and that their data is protected.

Flexibility & power

They are always looking for innovative new ways to serve their end users and did not want to create a new website with a short shelf life that would hamper their ability to deliver new features quickly.

This blog post will describe how Netlify and 11ty enabled the creation of their new website that gave 10x performance improvements, a Lighthouse Score of 99/100 for accessibility (more on that later) and a developer workflow that allows for multiple pushes a day to production.

Setting a benchmark with Core Web Vitals

When new work started on the Jamstack site we needed a benchmark to gauge how we are improving the site and helping their service users. Below is the Lighthouse Score for Blind Veterans UK’s old site that we took when we started building the new website:

Desktop score:

Original CWV Scores for desktop: 74 for performance, 64 for Accessibility, 80 for Best Practices, and 92 for SEO

Mobile score:

Original CWV scores for mobile: 25 for performance, 59 for accessibility, 80 for best practices, 84 for SEO

These were the stats that we were aiming to beat with the new site.

New site Core Web Vitals, after Netlify Migration

If you are a dessert before dinner kind of person, here are the stats for the new site built with Netlify:

Desktop score:

Core Web Vitals scores with Netlify: 100 Performance, 99 Accessibility, 100 Best Practices, 100 SEO

Mobile score:

Core Web Vitals scores with Netlify: 97 Performance, 99 Accessibility, 92 Best Practices, 100 SEO

Netlify tech helped us to get these amazing scores. Here’s how…

Accessibility

At Reason Digital we create a lot of charity websites, and we employ accessibility development to them all. Accessibility goes far beyond aria attributes. For example, we have worked with charities that have given us written scripts of what screen readers should read on each page, for each type of interaction. The difference between a <button> and an <a> makes a difference in how assistive technology reads and understands the page. Our QA developers have tools to blur the page during testing to make sure highlighted states and tab order are properly used.

The hunt for true digital accessibility is a never ending goal and knowing this, we needed to pick a technology for the site that allowed us to be as creative as possible when rendering pages to the user. Our own original research shows that the digital world is less accessible than the physical world, so we knew great care needed to be taken to avoid exacerbating this problem.

Blind Veterans UK’s staff were already familiar with a CMS so it made sense to stay with that provider as Jamstack is CMS agnostic. When it came to rendering technology we wanted as much control over the page as possible. We wanted to be able to fine tune each interaction very closely. We chose 11ty as it makes no assumptions and lets you code your own way. It simply gets data and merges it to make a static site. This allows us to control every aspect of accessibility as 11ty does not import any scripts automatically (like Gatsby for example). It only does what you tell it to do, and that’s exactly what we were looking for.

We’re big fans of making better HTML/CSS and using the browser, rather than including a giant JS project to only use a fraction of it. For this project we went for a No JS approach, making the page render fine without JS and then upgrading it later if the components needed it. This makes our First Paint incredibly fast, as any JS is deferred and not render blocking.

One example of an accessibility improvement came from user research. Some screen readers have a “region” shortcut that allows them to skip to important parts of the page. We wanted to implement this with certain components, and using Jamstack we could do this deterministically knowing that the HTML is not going to be modified post build.

<section aria-label="Section name">

   <!-- Or more complex components - ->

<section aria-labelledby="heading">
. . .
   <h2 id="heading">Section heading</h2>

The perfect score?

One of the things we learnt when we deployed the new site was how the WYSIWYG editor allows you to add headers out of order (e.g. H2 then H1). This is an accessibility issue, and makes it impossible for the site to score 100/100. We are working on a transform to re-order these to prevent errors in content entry.

The future

One of the great things about headless CMS’ and Jamstack is that the frontend developers have control over the content in a pragmatic way. We’re exploring, for example, the idea of using AWS Polly to convert paragraphs of text into MP3s that can be read by a user clicking a button. This is useful for people without a screen reader or who are new to screen readers. This is made much easier using a Netlify Function to call AWS which can convert the text in realtime and return the audio with a latency of about 1 second. As the paragraphs are returned as an array in JSON we can easily add buttons where we need them rather than in sections like menus. The buttons are then only rendered on demand and cause no lag in the page load.

Security

One of the great benefits of a statically hosted site is that all its traffic goes to the CDN, Netlify Edge. The origin server that hosts the CMS does not get any live traffic. On top of this we also added the latest security headers to make the browser as secure as possible:

image of security headers

(Taken from https://securityheaders.com)

To do this in Netlify is easy:

_headers

/*
 X-Frame-Options: DENY
 X-XSS-Protection: 1; mode=block
 x-content-type-options: nosniff
 feature-policy: microphone 'none'; camera 'none'
 permissions-policy: microphone=(), camera=()
 referrer-policy: same-origin 

As this site is purely informational we can tell the browser that no JS should access any other functionality (like the camera for example).

Netlify uses LetsEncrypt to give us a free SSL certificate:

A+ score from ssllabs

(Taken from https://www.ssllabs.com)

Flexibility & power

We wanted to be able to push multiple new features and not have to worry about the internal workings of the CMS, or whether a certain plugin in the CMS was going to clash with our new code. Netlify’s deployment system takes care of all the DevOps headaches and launches the Edge Functions for us automatically.

Fixing Cumulative Layout Shift (CLS) with Netlify Edge Functions

We wanted to show a cookie banner but we did not want Cumulative Layout Shift as a result (i.e. the page loads first, then checks for a cookie, and if it is not found then a banner is displayed to the user, pushing other page content down).

To solve this problem we used Netlify Edge Functions. Edge Functions allow you to very quickly process outgoing HTML before it reaches the user. In our case, we want to read the cookie header on the request (the browser will send this with all requests to your domain). If the cookie is set then we do nothing, and if the cookie is not set then we add the HTML to show the cookie banner. This way the banner is already there, or not, at the point the HTML reaches the user - no CLS!

Code sample:

netlify/edge-functions/cookie.js

export default async (request, context) => {


    if(`context.cookies.get("cookie-policy")`){


    	// Allow cookies


    }else{


        // Show cookie banner


    }


    return page;

}

Remove JS and CLS from page search with Edge Functions

Another feature that called for removing both JS and CLS was the site search. In an SPA a url such as ?search=my-text would first load the page, then that would fetch the results and render them in the page.

We wanted to prevent this, so we do the search in the edge-function and replace the results before we return to the user. We add a small amount of latency to the request, but the page returns fully rendered with all content.

netlify/edge-functions/search.js

export default async (request, context) => {
const url = new URL(request.url);


    if(`url.searchParams.get("keywords")`){


    	// Do search and inject results


    }


    return page;

}

Previewing Content with 11ty and Netlify Deploy Previews

One of the problems of making a CMS headless is that the built in preview functions of the CMS no longer work. Enter 11ty render and Netlify Functions to save the day.

Unlike edge functions, regular functions are just regular JS that returns data on its own endpoint. To get page previews working we needed to get 11ty to render just one page and return it.

netlify/functions/preview.js

const { builder } = require("@netlify/functions");

async function handler(event, context) {
const elev = new Eleventy("src", "dist", {
	// Add page preview data from CMS
});

const body = elev.toJSON();
//Find the page in 11TY in the array

return body[0].content.toString();


}


exports.handler = builder(handler);

Summary

By decoupling the design of the content from the content itself, we can control the pipeline much more closely. We no longer need to rely on the CMS, and this provides many benefits:

  • Speed: All pages are stored in a CDN and loaded instantly on request
  • Security: The CMS is hidden and all traffic is sent to the CDN
  • Flexibility: Every byte sent to the user is easily controllable, the Netlify platform takes care of the DevOps for rapid code deployments

Let’s have another look at the Lighthouse scores from the beginning of this article:

Desktop Core Web Vitals Scores (Before Netlify)

Desktop Core Web Vitals Scores

Desktop Core Web Vitals Scores (After Netlify Migration)

100 Performance score on Core Web Vitals after Netlify Migration

Mobile Core Web Vitals Scores (Before Netlify)

Core Web Vital performance score before migration, score of 28

Mobile Core Web Vitals Scores (After Netlify)

Performance score of 97 after Netlify Migration

These stats are an incredible improvement:

  • The First Contentful Paint is 2x faster
  • Time to Interactive 5x faster
  • Largest Contentful Paint 10x faster!

And best of all there were no compromises. The CMS layout editor is just as powerful as before, all we have done is take control with Jamstack and received orders of magnitude improvements!

Keep reading

Recent posts

How do the best dev and marketing teams work together?