Hey, Hey, Cloud Four is a PWA!
At 4/19/2024
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.Footnote 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"
}
Code language: JSON / JSON with Comments (json)
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 workFootnote 2 . 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.
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.
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.Footnote 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.
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:
- We wanted to dig deeper into how web notifications on the web work.
- 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
).
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');
Code language: JavaScript (javascript)
This allows OneSignal’s code to handle push notifications and our code to handle offline cache management.
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.Footnote 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.
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.
For more on using web notifications responsibly, we recommend Best Practices for Push Notifications Permissions UX by Owen Campbell-Moore.
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
Code language: Bash (bash)
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à!
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.Footnote 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.Footnote 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 c4site.staging.wpengine.com a Progressive Web App is an important milestone on our path.
Footnotes
- Moving to HTTPS meant a performance boost for our visitors when our host added support for HTTP/2 the week after our launch. Return to the text before footnote 1
- 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. :) Return to the text before footnote 2
- We found Eliminating Roundtrips with Preconnect by Ilya Grigorik invaluable in figuring this out. Return to the text before footnote 3
- “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?” Return to the text before footnote 4
- Figuring out how to make it easy to maintain in our mixed Drizzle and WordPress environment is the main challenge. Return to the text before footnote 5
- 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. Return to the text before footnote 6