Last month Microsoft released the long awaited Surface Duo, a foldable, dual-screen mobile device.
While it’s not (yet?) available in Australia, it didn’t stop me being interested in it, in particular because of what they are doing for web developers. You can read the full blog post here but the key points are:
- CSS primitives to detect the layout spanning mode
- CSS variables for screen and hinge dimensions
Basically, the browser sees both displays as a single viewport and it’s up to you to manage how that viewport is utilised, and in particular, how you manage the gap between them (which the browser doesn’t know about). Armed with this knowledge, I decided to have a look at how we can do responsive design and progressive enhancement for web applications, targeting a Surface Duo, using React.
Setting up an environment
As mentioned above, the Duo isn’t available outside of the US (at the time of writing), so how can we get up and running with it? With the browser dev tools of course! Here’s a blog about it all, but the way it works is the same way as any other mobile device emulation in Chrome or Edge, it’s just available*, so we can get started building an application.
*Note: This is still classed as experimental in the browser, so you’ll need to be running Edge or Chrome Canary, and enable it from
edge://flags. Read more about that here.
If you’re wanting to deploy this out to a wider set of users, but don’t want each one to configure their browser directly, you can setup an Origin Trial, which allows you to create time-boxed periods in which experimental features are enabled for your users. Check out this article on how to get started, and I’ve also added it to the demo app.
react-foldable is a series of React components and hooks that make it easier to work with a foldable device, using the proposed standards mentioned above.
Creating a foldable layout
My first goal is to look at how we can target the different displays with content, and react to the change, meaning that if we’re in a single display mode and “unfold” into dual-display, we want the ability to bring in more content.
We’ll start by creating a foldable zone in our application. This basically says that we’re going to be observing changes to the foldabilty of the device and reacting accordingly.
<Foldable> component we specify
<FoldableScreen>'s, which are added/removed from the component tree.
<FoldableScreen> needs to be told which screen to match. Non-foldable devices will always have a
0 screen, so that is where you’d put the things you always want displayed. There’s no restriction on the number of components you can have matching a screen either, as
<FoldableScreen> acts like a wrapper component to determine whether or not it displays.
Matching on a screen is good for a lot of common scenarios, but what if you’re wanting to conditionally show a component if the device supports dual screen or not? For this, we’d use the
match prop, like so:
match prop takes a function with the signature
(props: FoldableContextProps) => boolean, where
FoldableContextProps is defined like so:
Using this, we can completely remove a component if it’s in dual screen mode, allowing you to swap out large chunks of the component hierarchy.
While swapping components can work in many cases, sometimes you’ll want to programmatically detect the foldable information, and to make this easier there are a series of hooks. In fact, the hook values are all exposed through the
FoldableContextProps type on the
match as well, so the component dogfoods itself!
useDualScreen- a boolean to indicate whether or not the device is in dual-screen mode
useScreenSpanning- indicates whether the screen is horizontal, vertical or unknown (unknown is primarily when it’s not a foldable device)
useWindowSegments- returns an array of
DOMRectthat shows the bounding dimensions for each screen (non-foldable devices will return an array of one)
useFoldableContext- easy access to the React context containing all of the above values
This was a quick introduction to react-foldable, a library that I’ve been building to hopefully make it easier to create progressively enhanced applications for foldable devices using React.
You’ll find a demo of the component at https://react-foldable.aaron-powell.com/.
I’m very much open to feedback on how the component works and the general design, as right now it’s very much how I would tackle the problem, but if there’s aspects to prove on do reach out.