WaterBear: Building A Free Platform For Impactful Documentaries (Part 2) — Smashing Magazine

Created on November 12, 2023 at 10:49 am

WaterBear ORDINAL : Building A Free Platform For Impactful Documentaries (Part 2 CARDINAL )

17 min TIME read

Share on Twitter, LinkedIn

In this second ORDINAL article of a two CARDINAL -part series, Adrian PERSON shares insights about building WaterBear ORDINAL — his first ORDINAL project as a lead developer — discussing the challenges his team encountered while building it and various accessibility and performance improvements they’ve made. You’ll get a peek at the inner workings of a team striving to find its groove to systematize the work. In this second ORDINAL article of a two CARDINAL -part series, Adrian PERSON shares insights about building WaterBear ORDINAL — his first ORDINAL project as a lead developer — discussing the challenges his team encountered while building it and various accessibility and performance improvements they’ve made. You’ll get a peek at the inner workings of a team striving to find its groove to systematize the work.

In my previous article, I talked about Waterbear ORG , a significant project I worked on as a newly-appointed lead developer, and the lessons I learned leading a team for the first ORDINAL time. In this second ORDINAL article, I’ll go over some key technical highlights from the project. Before we start, let’s quickly remind ourselves what WaterBear LOC is all about and what makes it so interesting.

WaterBear is a free platform bringing together inspiration and action with award-winning high-production environmental documentaries covering various topics, from animals and climate change to people and communities. The WaterBear ORDINAL team produces their own original films and documentaries and hosts curated films and content from various high-profile partners, including award-winning filmmakers, large brands, and significant non-governmental organizations (NGOs), like Greenpeace ORG , WWF ORG , The Jane Goodall Institute ORG , Ellen MacArthur Foundation ORG , Nikon ORG , and many others.

For context, I am currently working at a software development company called Q Agency ORG based in Zagreb GPE , Croatia GPE . We collaborated with WaterBear ORDINAL and its partner companies to build a revamped and redesigned version of WaterBear ORG ’s web and mobile app from the ground up using modern front-end technologies.

In the first ORDINAL article, I briefly discussed the technical stack that includes a React-based front-end framework, Next.js for the web app, Sanity CMS ORG , Firebase Auth ORG , and Firestore database. Definitely read up on the strategy and reasoning behind this stack in the first ORDINAL article if you missed it.

Now, let’s dive into the technical features and best practices that my team adopted in the process of building the WaterBear ORDINAL web app. I plan on sharing specifically what I learned from performance and accessibility practices as a first ORDINAL -time lead developer of a team, as well as what I wish I had known before we started.

Image Optimization

Images are pieces of content in many contexts, and they are a very important and prominent part of the WaterBear ORDINAL app’s experience, from video posters and category banners to partner logos and campaign image assets.

Image assets, cards and carousel UI ORG elements are featured prominently on these promotional campaign pages. (Large preview)

I think that if you are reading this article, you likely know the tightrope walk between striking, immersive imagery and performant user experiences we do as front-enders. Some of you may have even grimaced at the heavy use of images in that last screenshot. My team measured the impact, noting that on the first ORDINAL load, this video category page serves up as many as 14 CARDINAL images. Digging a little deeper, we saw those images account for approximately 85% PERCENT of the total page size.

Unoptimized PERSON , full-size JPEG is around 1.2 MB QUANTITY . Imagine how poor the loading performance would be if we had to load a bunch of images like this one! (Large preview)

That’s not insignificant and demands attention. WaterBear’s product is visual in nature, so it’s understandable that images are going to play a large role in its web app experience. Even so, 85% PERCENT of the experience feels heavy-handed.

So, my team knew early on that we would be leveraging as many image optimization techniques as we could that would help improve how quickly the page loads. If you want to know everything there is to optimize images, I wholeheartedly recommend Addy Osami’s PERSON Image Optimization for a treasure trove of insightful advice, tips, and best practices that helped us improve WaterBear LOC ’s performance.

Here is how we tackled the challenge.

Meet “TypeScript in 50 CARDINAL Lessons”, our shiny new guide to TypeScript ORG . With detailed code walkthroughs, hands-on examples and common gotchas. For developers who know enough JavaScript to be dangerous. Jump to table of contents ↬

Using CDN For Caching And WebP For Lighter File Sizes

As I mentioned a little earlier, our stack includes Sanity’s CMS ORG . It offers a robust content delivery network ( CDN ORG ) out of the box, which serves two CARDINAL purposes: ( 1 CARDINAL ) optimizing image assets and ( 2 CARDINAL ) caching them. Members of the WaterBear ORDINAL team are able to upload unoptimized high-quality image assets to Sanity, which ports them to the CDN ORG , and from there, we instruct the CDN ORG to run appropriate optimizations on those images — things like compressing the files to their smallest size without impacting the visual experience, then caching them so that a user doesn’t have to download the image all over again on subsequent views.

Requesting the optimized version of the images in Sanity boils down to adding query variables to image links like this:


Let’s break down the query variables:

w sets the width of the image. In the example above, we have set the width to 1280px in the query.

sets the width of the image. In the example above, we have set the width to in the query. q sets the compression quality of the image. We landed on 70% PERCENT to balance the need for visual quality with the need for optimized file sizes.

sets the compression quality of the image. We landed on 70% PERCENT to balance the need for visual quality with the need for optimized file sizes. format sets the image format, which is set to auto , allowing Sanity to determine the best type of image format to use based on the user’s browser capabilities.

Notice how all of that comes from a URL that is mapped to the CDN ORG to fetch a JPG file. It’s pretty magical how a completely unoptimized image file can be transformed into a fully optimized version that serves as a completely different file with the use of a few parameters.

In many cases, the format will be returned as a WebP file. We made sure to use WebP because it yields significant savings in terms of file size. Remember that unoptimized 1.2 MB QUANTITY image from earlier? It’s a mere 146 CARDINAL KB after the optimizations.

With the aforementioned optimizations, we managed to reduce that unoptimized image size from 1.2 MB QUANTITY to 146 kB. QUANTITY That’s over 80% PERCENT reduction! (Large preview)

And all 14 CARDINAL image requests are smaller than that one unoptimized image!

The fact that images still account for 85% PERCENT of the page weight is a testament to just how heavy of a page we are talking about.

Another thing we have to consider when talking about modern image formats is browser support. Although WebP is widely supported and has been a staple for some time now, my team decided to provide an optimized fallback JPG just in case. And again, Sanity automatically detects the user’s browser capabilities. This way, we serve the WebP version only if Sanity knows the browser supports it and only provide the optimized fallback file if WebP support isn’t there. It’s great that we don’t have to make that decision ourselves!

Have you heard of AVIF? It’s another modern image format that promises potential savings even greater than WebP. PERSON If I’m being honest, I would have preferred to use it in this project, but Sanity unfortunately does not support it, at least at the time of this article. There’s a long-running ticket to add support, and I’m holding hope we get it.

Would we have gone a different route had we known about the lack of AVIF support earlier? Cloudinary supports it, for example. I don’t think so. Sanity’s tightly coupled CDN ORG integration is too great of a developer benefit, and as I said, I’m hopeful Sanity will give us that support in the future. But that is certainly the sort of consideration I wish I would have had early on, and now I have that in my back pocket for future projects.

Tackling The Largest Contentful Paint WORK_OF_ART (LCP)

LCP ORG is the biggest element on the page that a user sees on the initial load. You want to optimize it because it’s the first ORDINAL impression a user has with the page. It ought to load as soon as possible while everything under it can wait a moment.

For us, images are most definitely part of the LCP ORG . By giving more consideration to the banner images we load at the top of the page, we can serve that component a little faster for a better experience. There are a couple of modern image attributes that can help here: loading and fetchpriority .

We used an eager loading strategy paired with a high fetchpriority on the images. This provides the browser with a couple of hints that this image is super important and that we want it early in the loading process.

<!– Above-the-fold Large Contentful Paint image –> <img loading="eager" fetchpriority="high" alt="…" src="…" width="1280" height="720" class="…" />

We also made use of preloading in the document <head> , indicating to the browser that we want to preload images during page load, again, with high priority, using Next.js image preload options.

<head> <link rel="preload" as="image" href="…" fetchpriority="high" /> </head>

Images that are “below the fold” can be de-prioritized and downloaded only when the user actually needs it. Lazy loading is a common technique that instructs the browser to load particular images once they enter the viewport. It’s only fairly recently that it’s become a feature baked directly into HTML with the loading attribute:

<!– Below-the-fold, low-priority image –> <img decoding="async" loading="lazy" src="…" alt="…" width="250" height="350" />

This cocktail of strategies made a noticeable difference in how quickly the page loads. On those image-heavy video category pages alone, it helped us reduce the image download size and number of image requests by almost 80% PERCENT on the first ORDINAL load! Even though the page will grow in size as the user scrolls, that weight is only added if it passes through the browser viewport.

After scrolling the video category page, a bunch more images are loaded. We reduced image download size and number of requests by almost 80% PERCENT with a lazy loading strategy. (Large preview)

In Progress: Implementing srcset

My team is incredibly happy with how much performance savings we’ve made so far. But there’s no need to stop there! Every millisecond counts when it comes to page load, and we are still planning additional work to optimize images even further.

The task we’re currently planning will implement the srcset attribute on images. This is not a “new” technique by any means, but it is certainly a component of modern performance practices. It’s also a key component in responsive design, as it instructs browsers to use certain versions of an image at different viewport widths.

We’ve held off on this work only because, for us, the other strategies represented the lowest-hanging fruit with the most impact. Looking at an image element that uses srcset in the HTML shows it’s not the easiest thing to read. Using it requires a certain level of art direction because the dimensions of an image at one CARDINAL screen size may be completely different than those at another screen size. In other words, there are additional considerations that come with this strategy.

Here’s how we’re planning to approach it. We want to avoid loading high-resolution images on small screens like phones and tablets. With the srcset attribute, we can specify separate image sources depending on the device’s screen width. With the sizes attribute, we can instruct the browser which image to load depending on the media query.

In the end, our image markup should look something like this:

<img width="1280" height="720" srcset=" https://cdn.sanity.io/…/image.jpg?w=568&… 568w, https://cdn.sanity.io/…/image.jpg?w=768&… 768w, https://cdn.sanity.io/…/image.jpg?w=1280&… 1280w " sizes="(min CARDINAL -width: 1024px) 1280px, 100vw LOC " src="https://cdn.sanity.io/ GPE …/image.jpg?w=1280&…" />

In this example, we specify a set of three CARDINAL images:

Small: 568px , Medium: 768px , Large: 1280px .

Inside the sizes attribute, we’re telling the browser to use the largest version of the image if the screen width is above 1024px wide. Otherwise, it should default to selecting an appropriate image out of the three CARDINAL available versions based on the full device viewport width ( 100vw LOC ) — and will do so without downloading the other versions. Providing different image files to the right devices ought to help enhance our performance a bit more than it already is.

Improving CMS Performance With TanStack Query

The majority of content on WaterBear ORDINAL comes from Sanity, the CMS ORG behind the web app. This includes video categories, video archives, video pages, the partners’ page, and campaign landing pages, among others. Users will constantly navigate between these pages, frequently returning to the same category or landing page.

This provided my team with an opportunity to introduce query caching and avoid repeating the same request to the CMS ORG and, as a result, optimize our page performance even more. We used TanStack ORG

Query PRODUCT (formerly known as react-query ) for both fetching data and query caching.

const { isLoading, error, data } = useQuery ORG ( /* Options */ )

TanStack ORG

Query PRODUCT caches each request according to the query key we assign to it. The query key in TanStack ORG

Query PRODUCT is an array, where the first ORDINAL element is a query name and the second ORDINAL element is an object containing all values the query depends on, e.g., pagination, filters, query variables, and so on.

Let’s say we are fetching a list of videos depending on the video category page URL slug. We can filter those results by video duration. The query key might look something like this basic example:

const { isLoading, error, data } = useQuery ORG ( { queryKey: [ ‘video-category-list’, { slug: categorySlug ORG , filterBy: activeFilter } ], queryFn: () => /* … */ } )

These query keys might look confusing at first ORDINAL , but they’re similar to the dependency arrays for React ORG ’s useEffect NORP hook. Instead of running a function when something in the dependency array changes, it runs a query with new parameters and returns a new state. TanStack ORG Query comes with its dedicated DevTools ORG package. It displays all sorts of useful information about the query that helps debug and optimize them without hassle.

Let’s see the query caching in action. In the following video, notice how data loads instantly on repeated page views and repeated filter changes. Compare that to the first ORDINAL load, where there is a slight delay and a loading state before data is shown.

Now, we’ve optimized performance on the fetching side of things, the markup side of things, and the query side of things. That’s a nice spread of coverage the team can be proud of.

Accessibility Efforts

Accessibility is hard! That’s not to knock it, but to recognize the importance of all the work that it entails. The realm of accessibility is much bigger than making sure alt text is used on images and screen readers announcing things in order. It’s a practice unto itself and is directly influenced by the front-end work we do. Even unintentionally, our code can, at best, adversely impact a user’s ability to access certain content and, at worst, make it completely impossible to navigate a site with assistive technology.

This being my first ORDINAL significant project as a lead developer, I wanted the quality of the code my team writes (based on The Four Pillars ORG outlined in the first ORDINAL article of this series) to consider accessibility with special attention to not ruining something that already worked.

That extends to the decisions we make when implementing new features, particularly those that leverage third ORDINAL -party resources.

For example, it’s probably obvious that an app for documentaries is going to be designed around video. We made sure to choose a video player with a decent level of a11y support that allows users full keyboard control, uses clear focus states, adds basic aria-* attributes where necessary, includes focus-trapping within modal components, writes semantically-correct HTML… you get the idea. There’s a lot to consider.

Notice the prominent green outline on the focused card. Developers tend to hide the focus state, which is a bad practice. It’s more beneficial to customize it to fit the design. (Large preview)

We’re probably not even covering all of our bases! It’s so tough to tell without ample user testing. It’s a conflicting situation where you want to do everything you can while realistically completing the project with the resources you have and proceed with intention.

We made sure to include a label on interactive elements like buttons, especially ones where the icon is the only content. For that case, we added visually hidden text while allowing it to be read by assistive devices. We also made sure to hide the SVG ORG icon from the assistive devices as SVG ORG doesn’t add any additional context for assistive devices.

<!– Icon ORG button markup with descriptive text for assistive devices –> <button type="button" class="…"> <svg aria-hidden="true" xmlns="…" width="22" height="22 ORG " fill="none">…</svg ><span class="visually-hidden">Open filters</span> </button>

.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; white-space: nowrap GPE ; clip: rect(0 0 0 0); -webkit-clip-path: inset(50%); clip-path: inset(50%); }

Supporting keyboard navigation was one of our accessibility priorities, and we had no trouble with it. We made sure to use proper HTML markup and avoid potential pitfalls like adding a click event to meaningless div elements, which is unfortunately so easy to do in React.

We did, however, hit an obstacle with modals as users were able to move focus outside the modal component and continue interacting with the main page while the modal was in its open state, which isn’t possible with the default pointer and touch interaction. For that, we implemented focus traps using the focus-trap-react library to keep the focus on modals while they’re opened, then restore focus back to an active element once the modal is closed.

With focus trapping, we ensure that the browser focus stays inside the modal while it’s opened and is restored back to the previous element once the moda is closed. (Large preview)

Dynamic Sitemaps

Sitemaps tell search engines which pages to crawl. This is faster than just letting the crawler discover internal links on its own while crawling the pages.

The importance of sitemaps in the case of WaterBear LOC is that the team regularly publishes new content — content we want to be indexed for crawlers as soon as possible by adding those new links to the top of the sitemap. We don’t want to rebuild and redeploy the project every time new content has been added to Sanity, so dynamic server-side sitemaps were our logical choice.

We used the next-sitemap plugin for Next.js, which has allowed us to easily configure the sitemap generation process for both static and dynamic pages. We used the plugin alongside custom Sanity queries that fetch the latest content from the CMS ORG and quickly generate a fresh sitemap for each request. That way, we made sure that the latest videos get indexed as soon as possible.

Let’s say the WaterBear ORDINAL team publishes a page for a video named My Name PERSON is Salt GPE . That gets added to a freshly generated XML ORG sitemap:

This is an example of a dynamic sitemap for the latest video content. (Large preview)

Now, it’s indexed for search engines to scoop up and use in search results:

This is an example of the recently indexed video from the sitemap. Notice the “ 6 days ago DATE ” info, which corresponds to the lastmod ORG value in the sitemap. (Large preview)

Until Next Time…

In this article, I shared some insights about WaterBear ORG ’s tech stack and some performance optimization techniques we applied while building it.

Images are used very prominently on many page types on WaterBear ORDINAL , so we used CDN LOC with caching, loading strategies, preloading, and the WebP format to optimize image loading performance. We relied on Sanity for the majority of content management, and we expected repeating page views and queries on a single session, prompting us to implement query caching with TanStack ORG


We made sure to improve basic accessibility on the fly by styling focus states, enabling full keyboard navigation, assigning labels to icon buttons, providing alt text for images, and using focus traps on modal elements.

Finally, we covered how my team handled dynamic server-side rendered sitemaps using the next-sitemap plugin for Next.js.

Again, this was my first ORDINAL big project as lead developer of a team. There’s so much that comes with the territory. Not only are there internal processes and communication hurdles to establish a collaborative team environment, but there’s the technical side of things, too, that requires balancing priorities and making tough decisions. I hope my learning journey gives you something valuable to consider in your own work. I know that my team isn’t the only one with these sorts of challenges, and sharing the lessons I learned from this particular experience probably resonates with some of you reading this.

Please be sure to check out the full work we did on WaterBear ORDINAL . It’s available on the web, Android ORG , and iOS. And, if you end up watching a documentary while you’re at it, let me know if it inspired you to take action on a cause!


Many thanks to WaterBear ORDINAL and Q Agency for helping out with this two CARDINAL -part article series and making it possible. I really would not have done this without their support. I would also like to commend everyone who worked on the project for their outstanding work! You have taught me so much so far, and I am grateful for it.

( gg ORG , yk, il)

Connecting to blog.lzomedia.com... Connected... Page load complete