Hey, Hey, Cloud Four is a PWA!

Written by Jason Grigsby on

I’m thrilled to announce that cloudfour.com is now a Progressive Web App. Thanks to Erik, Gerardo and Nicole for the heavy lifting.

I want to share a bit about how we built our Progressive Web App in case it can help others. I’m going to focus on a high-level overview for now, but expect more details from my colleagues in future articles.

The journey started months ago

One of my favorite things about Progressive Web Apps is that they aren’t an all-or-nothing thing. When we worked on native apps, no one benefited until the binary was created and approved by the app store. That could mean months of work before it reached users.

Progressive Web Apps are different. You can incrementally add features to your site as part of your roadmap . And as you deploy each feature, your users benefit.

So when our new site design launched in July, we took the additional time to add HTTPS support. This simple act was our first step.1

Our manifest file

When the site launched, we also created a manifest file. We did this so that anyone adding the site to their home screen would see a nice icon. This is our current manifest file:

{
  "name": "Cloud Four",
  "short_name": "Cloud Four",
  "description": "We design and develop responsive websites and progressive web apps.",
  "icons": [
    {
      "src": "/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "theme_color": "#456BD9",
  "background_color": "#456BD9",
  "display": "standalone",
  "orientation": "natural",
  "start_url": "/",
  "gcm_sender_id": "482941778795"
}

This manifest file has remained mostly unchanged since the Summer. We changed the background-color, display, and added a push notification sender id (gcm_sender_id) to support the Progressive Web App.

Adding a service worker

In September, we had some time to work on adding a service worker to the site. Because we had already worked on a fairly complex service worker for Smashing Magazine, we were familiar with the technical parts. But we had some unanswered questions about the implementation details for our site.

What should get cached? What should be available offline?

Looking at our site, there was an easy list of things that we’d like to cache to speed up performance. Our CSS, JavaScript and any assets that were shared among the pages were obvious candidates.

When it came to supporting reading offline, how do we decide what to store in the cache? We debated pre-fetching the top-level pages in the background and stuffing them in the cache. Those pages change infrequently. And if they were cached, the site would feel more “app-like” because people could navigate the site offline immediately.

But the most view pages are our articles, not the top-level pages. And our articles are read by an international audience.

While it would be nice to show off how the Progressive Web App worked offline by caching top-level pages automatically, we know that most people who read our articles don’t go on to read about our approach or look at our prior work2. Automatically downloading those pages would be unexpected and unfair to people who might be using metered networking connections.

So we decided that caching pages as people visited them was the best compromise for our site.

Handling the offline experience

Once we decided on a strategy, we needed to figure out how to indicate to people that they are offline.

After the service worker has been installed, if someone is offline, we add a banner to the top of the page that lets them know that they are browsing offline. If we have the page in cache because they browsed to that page in the past, they get the full experience.

Our offline notification banner.

If someone offline browses to a page that isn’t in the cache, we have a fallback page that explains why they can’t view the content.

Our offline fallback page.

Deploying the service worker

There were other challenges we had to sort out before we deployed the service worker including:

  • What assets should we cache and how should we handle the CDN?
  • How to version the service worker?
  • How does the service worker fit in with our Drizzle + WordPress combination?

I’m sure my colleagues will have more to say about these issues in the future. Suffice it to say, each one of these presented some unexpected challenges.

But on October 3, we finished our work and deployed the service worker and our Progressive Web App launched.

Our site has been a Progressive Web App for two months

Because we already had a manifest file, when we deployed our service worker, the site became a Progressive Web App. We immediately saw improvements in the performance of the site due to the service worker.

However, we had more improvements to make so we didn’t announce the Progressive Web App immediately. (Let’s not mention how long it took me to finish this article.)

Improving site performance

As we worked on the Progressive Web App, we used Lighthouse frequently to measure our progress. Our site has always been fairly fast, but we wanted to improve the performance.

We recognized some areas for improvement in the way our fonts and stylesheets were loading. We were using Google Fonts for Source Sans Pro, and it was blocking rendering.

Erik made several improvements to our font rendering including:

  • Add Link HTTP headers for the domains that our site utilizes to make sure DNS lookups and HTTP connection are ready before the browser needs them to download assets.3
  • Adding <link rel="preload"> for our main font.
  • Embedding a subset of Source Sans Pro into our HTML to ensure that the browser had immediate access to the font and reduce FOIT.
  • Linking to the full font files to replace the subset.
  • Copying the font files to our own hosting and CDN so that it required fewer HTTP connections. This allowed us to leverage some of the benefits of HTTP/2.
  • Stashing the font file into the cache to ensure that the font loads quickly.

Testing with Lighthouse can be frustrating because results vary depending on conditions. But we’re consistently less than 2 seconds for first page load. And subsequent loads are under a second.

We've still got room to improve, but 99 out of a 100 isn't too shabby.

Web notifications

For Cloudfour.com, web notifications are superfluous. We don’t update the site often enough for it to be a huge value for our visitors.

But we decided to add them for two reasons:

  1. We wanted to dig deeper into how web notifications on the web work.
  2. We wanted to have an example of how they can be implemented responsibly.

We have much more to share about web notifications in the future, but for now, I’ll share the highlights of our implementation.

Using a push notification service

We decided to use OneSignal to provide the web notification services. We wanted a service that would allow us to see how many people had subscribed and be able to send test messages easily. It was much simpler to use OneSignal than to build all of that backend infrastructure from scratch.

OneSignal also offers a WordPress plugin that made it easy to send notifications when new articles are posted.

Finally, using OneSignal allows us to support Safari’s non-standard push notifications.

Two service workers

The biggest hurdle in implementing OneSignal was that we were already using a service worker. Our service worker conflicted with OneSignal’s service worker and vice versa because they had the same scope.

Gerardo spent quite a bit of time working with Jason Pang of OneSignal to find a way to modify both service workers so that they could co-exist. Jason went above and beyond in helping us out. We’re incredibly grateful to him.

If you never sign up for web notifications, you’ll only get our service worker (sw.js).

You can see the active service worker in the Applications section of Chrome Dev Tools.

When you sign up for push notifications, OneSignal’s service worker takes over and imports our service worker:

/* v0.1.5 */
importScripts('/sw.js');
importScripts('https://cdn.onesignal.com/sdks/OneSignalSDK.js');

This allows OneSignal’s code to handle push notifications and our code to handle offline cache management.

After you sign up for push notifications, you can see OneSignal's service worker in the Chrome Dev Tools.

Using notifications responsibly

We see an increasing number of websites using notifications irresponsibly. The moment you visit the site, you get prompted by the browser to sign up for notifications. Not only is it annoying to be asked immediately, but it is unlikely to be effective.4

We wanted to set a good example for responsible use of push notifications. So we don’t ask for permission to send push notifications until the user selects the button asking to turn notifications on. And we only provide the option to subscribe at the bottom of articles.

Screenshot of our options to subscribe to new articles via either web notifications or email

Once someone selects that button, then we trigger the browser alert asking for permission to send notifications. If the person signs up, we send a quick notification thanking them and update the button to indicate how they can turn off notifications.

Our Get Notifications button turns into a Turn Off Notifications button.

For more on using web notifications responsibly, we recommend Best Practices for Push Notifications Permissions UX by Owen Campbell-Moore.

Our welcome notification on Android.

Add to home screen prompt

The last piece of the puzzle was verifying that Chrome and Opera on Android would prompt people to add our site to their home screen.

Because the browser heuristics for prompting users are changing, the easiest way to test is to turn off those heuristics via a browser flag.

chrome://flags/#bypass-app-banner-engagement-checks
opera://flags/#bypass-app-banner-engagement-checks

After turning on that flag, I found that Chrome wasn’t prompting for app install. Thanks to Alex Russell who pointed out that our manifest file declared the display mode as browser instead of the required standalone or fullscreen.

A quick change to the manifest and voilà!

Add to Home Screen banner for Cloudfour.com in Chrome and Opera on Android.

Continuing towards a better Progressive Web App

Of course, the great thing about the web is that it is never done. There are many more improvements we’d like to make including:

  • Deploying critical css to add one more performance boost.5
  • Determining when to remove items from the cache.
  • Deciding if applying some form of app shell would make sense for our site.
  • Making it easier for people to find out where to turn off notifications.6

I’ve said before that the path to a Progressive Web App is much more important than the destination. Progressive Web Apps are about providing a better and faster user experience. There is no end destination when improving user experience is your goal.

But just for a moment, I wanted to stop and recognize what our team has accomplished. Kudos to them. Making cloudfour.com a Progressive Web App is an important milestone on our path.


  1. Moving to HTTPS meant a performance boost for our visitors when our host added support for HTTP/2 the week after our launch. 
  2. We’d love it if they did though. We’d love it even more if they contacted us to do some work for them. Hint, hint. :) 
  3. We found Eliminating Roundtrips with Preconnect by Ilya Grigorik invaluable in figuring this out. 
  4. “Why yes website owner, I just landed on your site for the first time ever, but you’re right that the first thing I want to do is give you my location. Oh, notifications? That too. Access to my camera? Of course. Anything else you need from me before I look at this page?” 
  5. Figuring out how to make it easy to maintain in our mixed Drizzle and WordPress environment is the main challenge. 
  6. This is important because as Owen Campbell-Moore notes, “Chrome provides users a kill switch that disables all notifications from a specific site. If used, this site will never be able to send the user notifications again.” We want people to turn off notifications via our site, not the kill switch. 
Jason Grigsby

Jason Grigsby is one of the co-founders of Cloud Four, Mobile Portland and Responsive Field Day. He spends far too much time obsessing over mobile and the web. Follow him at @grigs.

Never miss an article!

Get Weekly Digests


Comments

Add a comment

Have you considered using a 3rd-party js prompt-to-add-to-homescreen library for browsers that don't support the manifest, but do have good add-on homescreen support? Cough Safari iOS Cough.

Replies

I haven't thought about it too much. My understanding is that there is no way to trigger add to home screen from JavaScript. That's why you see sites with pop ups pointing to where they assume the button is going to be for adding to home screen. I find those solutions to be obnoxius.

In truth, while I think it is cool that if someone spends a lot of time on our site that the browser will ask if they want to add it to their home screen, I'm not particularly interested in having people add our site to their home screen. If I didn't work for Cloud Four, I wouldn't have it on my home screen.

I think providing ways to subscribe (notifications, email, rss, social, etc.) are much more useful for the people who visit our site.

I’m curious what you're doing with images (especially responsive images). A quick look at your (readable!) sw.jsindicates... nothing special (other than going offline first, rather than online first, with responses).

In testing, have you run into these issues: https://developers.google.com/web/fundamentals/getting-started/primers/service-workers#handling_responsive_images ? Basically with a multi-resource responsive image, it’s possible to cache one URL, and then later have the device request a different one. Subbing the wrong-sized cached resource is better than serving up a blank fallback, but adds complexity. My thought upon pondering this is that it’s probably not a very common case, as it only occurs in cases of window resizing, or some other change in context on the same device, so I have wonder whether or not dealing with srcset-based resources in special ways is worth the complexity.

Replies

Sadly, at the moment we're not doing anything at all with responsive images right now. :(

Originally, I had planned on offloading responsive images to image resizing service provider that does all sorts of fancy things like picking image breakpoints based on performance budgets, but I realized right before the site launched that their WordPress plugin was broken.

I haven't had time yet to dig into what to do given that doesn't appear to work. I'm not going to manually resize images.

So the question of what to do with responsive images for the service worker isn't something we've had to worry about yet.

Hello Eric, it's interesting that I read your comment the exact day I try to cache Cloudinary images on my own blog! ;-)

I don't do Art Direction (yet), so I've not the main issue explained in the page you link to.

But I'm stuck even before this, because the image sometimes goes to the cache, as Chrome devtools show, but it is never served when I'm offline.

My Service Worker is heavily based on Jeremy Keith's one, network first for HTML and offline first for other resources.

My most recent post is a good example.

This was a really great read however there are two things I want to mention.

Why would you want to implement Critical CSS? It would only benefit people with browsers that don't support HTTP 2.
I noticed that the button to enable and disable push notifications becomes unresponsive very quickly on Android. If you switch to a nother app or tap a link and navigate back to your article you can't enable or disable the notifications. But I think that might have to something with Android and Chrome itself. If you reload the page it works again.

Leave a Comment

Please be kind, courteous and constructive. You may use simple HTML or Markdown in your comments. All fields are required.


Let’s discuss your project! Email Us