← All posts

Cova's Design

· Rohan

Columbus and Babel.

A few years ago, I watched Columbus, by Kogonada. It left me astounded in its beauty, that I described in my short Letterboxd review as

Take a photo of any scene in this and you’re getting the coolest photo ever

I’ve never really stopped thinking about the beauty of Columbus. This feeling recaptured me once again recently, in my reading of Babel, by R.F. Kuang. An incredibly interesting novel with such captivating discussions of language and translation, and a book that made me feel the same way Columbus did, but in the written form. In a similar vein, I felt like you could take any excerpt from the novel, and come out with a thoroughly beautiful and thoughtful quote.

This was the driving force behind so many of the design decisions I made for Cova across the board. The concept of every pixel, every screen, every interaction - all evoking this feeling of creating a lasting impression from a single moment. In so many ways, I truly doubt Cova will ever reach my lofty ambitions. Much of the app was built quickly, at a pace where I realistically could have released it in early June. I was continually striving for perfection, though, perhaps to my and Cova’s detriment.

Cova is the largest project I’ve worked on across engineering, design, and product, and has continually evolved over the past few months into what it is now. Convictions I had early on have since been completely reversed, features I never thought of early on have quickly become must haves. Living with Cova for so long made it feel like a child, and it’s difficult to see the shortcomings of something that you’ve had so close to you. But one of the early questions I received from one of my friends after testing the app, “What is a cove?”, made me rethink my perspective, and how well I was communicating to the user what Cova truly is.

Inspiration

From the outset, I knew that I wanted Cova to feel like home, and as such, I drew upon inspirations from what I call home myself. The color palette is heavily inspired by colors I associate with what I grew up with - beaches, fog, forests. Beyond just the color scheme, I wanted Cova to feel like a place that would lead the user away from screens, and into their community. This was a huge reason behind incorporating various nature aspects throughout the app, such as small moss growing on block-shaped components, grassy fields with trees at the end of scrollviews, etc. Capa (the bear) was one of the aspects I wanted to incorporate from day one - a fun, simple mascot for the app. All of these aspects were wonderfully drawn by my sister to add a fun, cute flair to the app.

Gesture first, single plane

Cova is built on a gesture first paradigm, where many actions can be done via gestures, rather than simple taps. At a very high level, I wanted the app to intuit what the user wanted to do, based on the gestures they used. The feed was a very early idea I had for how I wanted Cova to operate - a horizontal feed of cards that can be swiped up to expand the content of the card. As a single plane of content for the user, the feed acts as a simple anchor for the user to do any action, as the user never really navigates away from it at any point. Beyond the gesture-first approach, I was also convinced that I absolutely did not want to incorporate a tab bar in the app. My hope was that the user’s context would never need to switch - you would always be a tap away from the feed and the core actions within the feed.

These tenets naturally presented a large conflict from day one - while this CX was intuitive to me, how would I teach my users on how to use Cova? How does a user discover what they can do? How do I make this experience accessible?

Discoverability

When a user first lands on the Cova feed, there are a few immediate visual affordances I built into the CX:

  1. The expandable aspect of cards is implied by cutting off the content at the bottom behind a gradient
  2. The peek of the next card in the feed

While these are implied hints as to how to use Cova, it’s also subtle - both hints are visual, and could easily be missed by the user. To address this, I utilized motion to teach the user of the action. Slight animations that play (until the user actually executes the action) to show that the card can be expanded, and the feed can be swiped, were implemented to clearly communicate the actions that can be taken. This avoids an explicit tutorial of the CX, and doesn’t disrupt the customer flow while still ensuring they are aware of the available actions.

Accessibility

When designing this unique feed approach, one of the primary concerns immediately I had was how to make the experience fully accessible. My goal was to reach WCAG accessibility AA standards. Certain aspects were easily adjusted (color contrast, text size, etc), but screenreaders and voice control on the OS level contributed a larger level of complexity. Rather than try and make a single implementation that compromises and works decently for both cases, I instead wanted to treat both cases as the primary use case. Every user counts - and the app should reflect that. To do this, I implemented a parallel path that users utilizing screenreaders can use, a transparent overlay over the feed that acts as a mirror to the physical feed visual users are presented with. This transparent overlay acts as an intermediary for screen readers, identifying intent, and clearly communicating the content and state of the current cove via a composed, summarized label. Screenreader-gated scroll actions are utilized to tab through coves in the feed where swiping may not be possible.

A Native-Feeling Experience

At the end of the day, I wanted the CX to feel as native as possible - something that detractors of React Native will commonly point to as a limitation of the platform. My goal was to prove this wrong. I upheld a strict standard of moving animations away from the JS thread as much as possible, and onto the native UI thread. In many ways, this approach, and the potential limitations of RN, pushed the Cova architecture to be much more robust.

One area I became obsessed with over the past few months was the feed performance. I wanted it to feel buttery smooth. A primary approach I took for this was to utilize a single animation value derived on the UI thread to track all related animations in the feed. For example, the “simple” expand/collapse of a feed card actually influences a variety of other animations that have to be seamless (the Map/Filter FABs, the distance pill fading in/out, etc). By deriving all of the animations from a single source of truth, I was able to ensure that all of them remained in sync, and also reduced the computational overhead of maintaining multiple animation values to track.

Perhaps even more interesting was the Map view CX I developed. In this, I also wanted to stay gesture first (while maintaining legitimate “easy” options via regular buttons). So coves in the map view could be swiped back and forth from the collapsed view, and then swiped up/down to expand/collapse the current cove’s details. Getting this smooth, and more importantly performant, was a ridiculously difficult task. The feed cards had a bit of an easier approach, as the above the fold content was already present and loaded before the animation starts, so for the user, the expand animation is fluid. But in the map view, the collapsed CX only displays the minimal information for the cove, and expanding displays a wildly different CX to the user. To make this transition seamless, I utilized a variety of tricks, significantly deriving animation values not from a JS-thread hosted stateful value, but rather the sheet’s height itself. This, of course, introduced another major complication - the CX was subsequently rerendering on every height change of the sheet, no matter how minuscule, resulting in rerender storms and dropping JS thread FPS. A simple solution was to throttle that calculation to only significant, multi-pixel sheet height changes.

Cova

Cova is an important culmination of a lot of stuff - my career over the past few years, the influences of nature throughout my life, how important community is for everyone. My hope is that as new users explore it, they’ll get to delight in the CX, and hopefully be inspired the same way art like Columbus and Babel inspired me.