Progressively Enhancing Desktop Devices

Wednesday, 24 July 2019

As we began experimenting with bringing the Twitter PWA to desktop users we received heaps of feedback around the look, feel, and of course a bunch of missing features. It was clear that before we could bring the rest of the world into the new Twitter web experience, we needed to consider: who was this desktop user, what makes them distinct, and whether there is a way to codify these differences. 

There’s more to our desktop Twitter Web users than just simply thinking they have more screen real estate. In fact, we break things down into many different features (such as their input modality) and use them to progressively enhance the site. We consider whether they are using a wide or narrow screen, whether they’re touching to navigate or clicking with a mouse, whether or not there’s a keyboard, whether there’s a reliable network, whether user has chosen to turn data saver on, and so on. In practice, any combination of the above is possible, so we’ve designed our components to make the most of what’s available.

These components react to many different environmental factors, allowing us to bring a better Twitter experience to a wider range of mobile devices across Android, iOS, Windows, and more.  As we thought about what we wanted our web app to be, we realized that removing the binary split between mobile and desktop would enable us to also cater to hybrid devices: modern tablets having keyboards, or low-cost, small mobile devices without a touch screen, all within one app.

In this blog post, we’ll cover a few of these components that demonstrate how progressive enhancement in our components has helped the Twitter PWA support a growing spectrum of web-capable devices.


Action sheets

When there’s too many options to display for a piece of content, like a Tweet or event, our native counterparts show these options as a half sheet, an overlay that takes up, you guessed it, half the screen. It’s a widely used pattern for mobile devices. 

On the desktop website, where we’re not as restricted by screen real estate and users often expect menus to appear closer to where they click, we opted to display these options as a dropdown that is positioned relative to the clicked content.


This post is unavailable
This post is unavailable.

Because the conventions are different between mobile and desktop users, and we’ve consolidated our code into one codebase, we need a component that is smart enough to know when to display as a half sheet vs a dropdown. We call this our ActionSheet.

After checking the width of the window, we decide which experience to present the user.

It’s important to note that in general we try to avoid thinking of these experiences as mobile or desktop since tablet and mobile screens are getting larger. Because of this, we make the decision based on width rather than device. 

Each ActionSheet is comprised of many ActionSheetItems, each of these in turn contains a link, title, and icon property. The ActionSheet also adds in a cancel button at the end of the list of ActionSheetItems, but it only does this on mobile. Again, this pattern is familiar for touch devices, but on desktop (or more specifically for dropdowns), people are used to just clicking outside to dismiss it. 

When it comes to adding a new use of the ActionSheet component, our team no longer has to worry about coding in the functionally for wide vs narrow screens each time. They simply create the array of elements and pass it to the ActionSheet component, worry free. 


Master detail and sidebar

As we began using on larger screens and testing the experience internally, we received a lot of feedback about how vast and empty many of the core pages felt. 

We started finding opportunities to create a more optimized view for the wider screens that gave more context and allowed for multitasking. One of the patterns that emerged from this was a Master-Detail container. We saw this pattern in our settings and direct messages pages, where a user was able to quickly traverse a list of items in the master column while also viewing more detail in the secondary column.

This post is unavailable
This post is unavailable.

This pattern is handled purely at the routing level. We use react-router, which has two relevant components, Switch and Route. Routes generally sit within a Switch which matches their path against a list of known routes to decide which component to render. 

For MasterDetail, we introduced a special type of Switch, that instead of matching a single route, can match two routes: a root route (shown on the left above) and the page specific route (at right). This allows us to display what would normally be two different pages on a narrow device, side-by-side when the user has a larger display.

We used this same technique to adapt certain screens to be modals on wide-screen devices. Our ModalRoute component looks just like a normal Route, but decides whether to render as a modal or as a full screen page by checking the window’s width. ModalRoute components also have a lot of handy functionality built-in, like clicking outside to dismiss, displaying the last page in the background, or applying accessibility props for our screen reader users.

Now, adding a new modal or page to the new Twitter Web app isn’t a daunting experience. You just change your Route component to a ModalRoute component! We can reliably trust that the ModalRoute is handling the switch between screen (for narrow windows) and modal (for wide-screens), and that all the associated benefits we’ve built into Modals will be there.


Keeping it quick

With all the work we put into the desktop experience, we added a lot of code to the site. As we did this, we wanted to make sure we didn’t lose sight of our goal of keeping the mobile site fast. Much of the code on the website may never be accessed in a session. For instance, if you don’t crop images, you won’t see the image cropper, and there’s a whole bunch of settings you may not access day to day.

In fact, although much of our work has been focused on wide-screen desktop features, we have to be aware that narrow-screen users may never see them. We had to consider this when designing our SidebarLayout, which allows us to display related content to the side of another page. However, this content is only shown when a user’s display is wide enough.

This post is unavailable
This post is unavailable.

Since this content may or may not be displayed, we again use our handy window width checker. On narrow screens we can avoid loading any code or content that would be in the sidebar.

This technique doesn’t end at the sidebar. In fact, anything that might keep you from getting to the main content fast is rendered using a LazyLoader. The composer is a great example of this. Its sub components include gifs, the emoji library and DraftJS, which are all needed before we can activate that component. As a result, we wrap all of the composer elements with a LazyLoader so that you’re not waiting on the gif picker or image cropper to load before you’re able to look at Tweets. 

Finally, we also take into account your connection to the internet when deciding how to render media-rich content on the site. Some of these optimizations can be manually enabled with our data saver mode. This will reduce the number of network connections we make, allow you to explicitly choose which images to download, and generally reduce contention on slower networks. Other decisions might be made behind the scenes. For instance, if we detect that you aren’t able to upload as quickly or reliably as normal, we may reduce the size of your Tweets images. This ensures that you can still get your message out to the world. When we first tried this, we saw failure to send tweets with media reduced by 10%! These are just a few examples of things we do to quickly get you to the content you care about.



Taken together, features like those mentioned above allow us to progressively enhance the Twitter web experience -- adjusting to users’ devices to get just the right experience to everyone. They enable us to give desktop users a great experience without sacrificing load times and without negatively impacting the experience of our mobile users. We’ve only just started to scratch the surface of what’s possible using these techniques, and are excited to continue improving on new and old features alike, taking advantage of what we’ve learned so far.

This post is unavailable
This post is unavailable.