Hubs-Foundation / hubs

Duck-themed multi-user virtual spaces in WebVR. Built with A-Frame.
https://hubsfoundation.org
Mozilla Public License 2.0
2.13k stars 1.42k forks source link

Room UI Redesign Accessibility: Focus Layers #3477

Open robertlong opened 3 years ago

robertlong commented 3 years ago

Background

In the redesign, we're putting in the work to ensure that Hubs has excellent keyboard navigation support for accessibility. Currently, all of the new elements have proper focused element styling and tab index ordering is getting close to correct. However, one area we are severely lacking is trapping focus when a modal, popover, or other full screen element is visible.

From the WAI-ARIA Authoring Practices Document:

A dialog is a window overlaid on either the primary window or another dialog window. Windows under a modal dialog are inert. That is, users cannot interact with content outside an active dialog window. Inert content outside an active dialog is typically visually obscured or dimmed so it is difficult to discern, and in some implementations, attempts to interact with the inert content cause the dialog to close.

Like non-modal dialogs, modal dialogs contain their tab sequence. That is, Tab and Shift + Tab do not move focus outside the dialog. However, unlike most non-modal dialogs, modal dialogs do not provide means for moving keyboard focus outside the dialog window without closing the dialog.

I've began to implement focus-layers in Hubs, but I've ran into a few design issues and I'd like to get some feedback on my implementation before continuing.

Focus Layers

The team at Discord have developed a library called focus-layers that helps implement these sorts of focus interactions in React. As a developer, I can add focus layers to components like Modals and Popovers and the focus will be isolated to those components. When the component unmounts (is removed from the DOM) that layer will be popped off the focus layer stack, my focus will return to the previously focused element in the parent layer, and I'll be able to focus items in the parent layer again. It's a great library that manages to simplify a really hard problem.

The redesign has quite a few components that trap focus. Here's a diagram of them: Focus layer diagram, containing canvas, ui root, fullscreen layout, modal, sidebar, and popover components

Canvas

We can probably think of the canvas / 3D viewport as the bottom most focus layer or perhaps it's not really a focus layer at all. If it is thought of as a focus layer and if you have nothing focused in the UI, your keyboard input should go to it. If it's not a focus layer, then we can send all of the global keyboard shortcuts to the canvas so long as an input element like a text input isn't focused. This is somewhat complicated by the fact that we have bound the Tab, Space, Escape, and Arrow keys to actions in the 3D viewport.

Two solutions for this that I've come up with are:

  1. The canvas element takes focus and 3D viewport hotkeys are only received when it is focused. This means we can avoid remapping most of these hotkeys. However we probably want to remove the keyboard mapping for tab so that users can change focus to elements in the UI root. Pressing the escape key in the UI root, tabbing to it, or clicking on the canvas would focus the canvas.

  2. The canvas element takes focus, but the 3D viewport hotkeys are bound globally. The Tab and Spacebar keys would be unbound. Escape would be used in both the 3D and 2D context depending on what is in focus to. For example, it is used in to cancel drawing with the pen. The arrow keys would be bound to movement when the canvas is focused, or whenever we can detect that you are a "mouse user". Currently you start out as a mouse user and a keyboard user is detected when the user presses the tab key. Clicking anywhere on the page switches you back to a mouse user. With this method, you can use the arrow keys to move around in 3D space and still use them to scroll through lists, etc. The WASD keys are globally bound and can always be used to move around in 3D space.

UI Root

The UI Root component is, as it's name suggests, the root of the application. When you load a Hubs room and press tab, the first element that is focused, is the join room button.

Screenshot of the room entry panel with the join room button focused

In the above screenshot, the join room button is focused, and you can move focus to the bottom toolbar. For example the next focus item after the "Options" button is the "Invite" button in the bottom toolbar and the item after the "More" button in the bottom right, is the "People" button in the top right.

Once you are in the room, there are more items in that bottom toolbar that can be toggled or open popovers/sidebars.

In-room screenshot with additional toolbar items

Popover

The popover component traps focus when opened. Focus currently starts on the close button, but when properly implemented will focus the next element. Pressing tab repeatedly will eventually reach the end of the focusable elements in the popover and loop back around to the first focusable element. Pressing the escape key or unmounting the popover (triggered via the close button or another action) will untrap the focus.

Screenshot of Hubs with the Popover open and the close button focused

Modal

Modals work the same way as popovers, except sometimes they can't be closed via the escape key.

Screenshot of the close room modal

Fullscreen Layout

The focus layer for the fullscreen layout component also works the same way as modals and popovers, but it always takes up the whole screen and obscures the rest of the content. The media browser, avatar editor, and preferences page are examples of this component.

Screenshot of the media browser

Sidebar

The sidebar component only traps focus when it is full screen. On mobile devices or smaller browser windows, the sidebar is full screen and obscures the other content. Focusing elements outside the sidebar when it is fullscreen wouldn't make sense because you can't see these items, so we only enable the focus layer when it is fullscreen. When the focus is inside the sidebar, you can press escape to hide it or navigate backwards.

Sidebar on a large screen

Sidebar on a mobile device where it takes up the fullscreen

Summary and Action Items

Overall, focus layers allow for easy and correct keyboard navigation, but I could use more opinions on where they are used, how they are implemented, and for someone to check my understanding. I would especially like input from someone who has extensively used or studied interfaces that implement these patterns correctly, because I'm still pretty new to this.

The biggest design decision is how to handle the 3D viewport keybindings in combination with the standard keyboard navigation controls. I haven't fully thought through either of the two options I've proposed, but I think they are good starting points.

I'm going to pause on the technical work until we've had time to discuss the design, if anyone would like to see a demo, you can check out that branch or we could stand it up on our staging server. I'm happy to walk people through a demo.

┆Issue is synchronized with this Jira Task

mbransn commented 3 years ago

Assigning this to @mcroud for review and UI recommendation.

DPX-designer commented 3 years ago

Hi @robertlong

Thanks for this! A few questions if I may. I'm trying to visualise the conundrum of managing focus between 3D world and 2D UI. In the image below have I captured the two options accurately?

Frame 6.jpg

Would method A prevent us from using tab as a means of cycling interactive objects in the 3D World? I've posed the question of tab use in 3D environments to #accessibility, perhaps someone has some advice on expected tab behaviour there. Perhaps keyboard users would have a fixed cursor allowing them to interact with objects by proximity or simply centralising them in their view, removing the need for tab in this manner?

robertlong commented 3 years ago

Yes, I believe method A would prevent us from using tab to cycle 3D object selection. However, there is still the object list in the 2D UI. I think we want to bring most, if not all of the 3D UI into the 2D object menu. So, I think using the object list to navigate objects in the 3D world would work well. Same thing goes for people.

For B, I think we would need to break up the focus in the 3D world. If you had 100 objects in the world that wouldn't be very intuitive. As I understand it, you would use a list and navigate with the arrow keys when you have a lot of selectable items.

Perhaps we could introduce a hotkey to open and focus the objects/people list in method A. That way keyboard users could quickly navigate to selecting an object. Also, the fixed cursor would be great as well. I think it would be also be interesting to see if the option for a first person view with cursor lock on the canvas would be interesting to those users and the section of our userbase familiar with games. I think borrowing mechanics from a FPS control scheme would be a pretty understood method of placing a cursor in the middle of the screen while maintaining the ability to look and move around.

DPX-designer commented 3 years ago

Yes! the Objects list is a very good shout!

I'm trying to source any existing research material or observations we may have with 3D/VR and keyboard usage. I'm afraid I haven't ventured far beyond typical keyboard usage on a website/app so on one hand I wouldn't want to weigh in with impulsive opinions, however I have spent a good chunk of today with the new UI on keyboard and did encounter moments where the behaviour was unexpected or I lost my place.

I don't see a deadline but I'm guessing that the immediate conundrum needs to be resolved prior to the new UI going live? I ask because this task seems like it could be part of a larger accessibility audit. With the obstacles I encountered I wonder if a heuristic evaluation would highlight pressing issues.

Stepping back to the problem at hand, the Object list is a terrific addition and using this with the "view" button (personal opinion, "go to" would feel more accurate to me) provides a lot of control and flexibility for future concepts.

Screenshot 2021-01-08 at 17.49.29.png

Making this task more efficient for keyboard use feels like a good measure to me, so the hotkey for the Object list feels like a worthy addition.

robertlong commented 3 years ago

After speaking with James Teh, we decided to push the rest of the accessibility efforts out a bit. We really need to get them right and not rush through it. We've made big strides already, but as you saw, these focus layers are necessary for things to work well. So let's not focus too hard on a tight deadline and instead focus on finding the correct implementation. I don't think there are huge remaining engineering problems. The design system as a whole is mostly accessible, but it falls apart when composed into larger flows due to this focus issue.

Agreed, with the object menu. That's an area of the UI I'd like to put more focus on in the future. Also, "View" was originally "Go To", I'm happy to switch it back.

DPX-designer commented 3 years ago

Understood.

I wanted to know what the expected keyboard patterns would be for a user, so I found https://webaim.org/techniques/keyboard/#testing and began using Hubs to see if that might shed some light.

I took the mindset that if a canvas hotkey doesn’t impact or conflict with an access key for the current focused element then allow it to affect the canvas.

I tried to navigate the object list using the Up/Down arrow keys, it had no effect (not that up/down is mandatory in this scenario) but in the background I was moving in the 3D world. Similar with arrows keys and the main menu.

Learning of other accepted keyboard patterns, like using [J] and [K] for next and previous (Twitter/Tumblr/Google), I wondered whether treating the canvas as a separate layer to the UI may allow us to utilise more shortcuts in the future without fear of conflict or unexpected results for users.

Similar to a large application like Google Docs, perhaps we use Tab to move focus between the main sections of the app, and then use arrow keys to navigate inside those areas. I.e Tab moves between Canvas > Main menu > Object/People list. The arrow keys then traverse the items within those sections. This might also help us overcome traversing a large object list as we can “jump out” with a single tab press. Slack also allows this broad section jumping with Command + F6.

I know this is beyond what we are talking about here but expanding on the object list hotkey, maybe utilising the “/ ?” Key to bring up a simple shortcut menu as per other web apps would allow us to provide more specific key combinations to access certain features.

There are a good few people in DPX who would be very happy to participate in strategic discussions with Hubs on validating accessibility patterns when the time comes for a wider effort.

robertlong commented 3 years ago

Removing P0 tag as this isn't a bug but is being prioritized for design and development.