RoverProvider
An accessibility pattern for a grouped set of inputs
- Use the tab key to navigate to the Menu.
- Then, use the
ArrowUpandArrowDownkeys to go through each option. - Try pressing
HomeorEndto jump right to the first or last elements on the group. - Press
tab(orshift+tab) again to exit.
Development Instructions
1. Wrap each roving tabindex group in a RoverProvider
You can nest roving tabindex components in other DOM elements or React components.
<RoverProvider>
{..content here}
</RoverProvider>
You can also choose the direction of the navigation. It can either be "vertical" (default) or "horizontal".
<RoverProvider direction="horizontal">
{..content here}
</RoverProvider>
Choosing between "vertical" and "horizontal" implies you can use:
horizontal- "ArrowLeft" and "ArrowRight" keysvertical- "ArrowUp" and "ArrowDown"Homekey to go to the first elementEndkey to go to the last element
2. Wrap each focusable element
For composition, try to identify which elements are the ones that are going to be affected by the RoverProvider.
For each one of those, wrap them with your own component and use the useRover and useFocus hooks.
const MenuButton = ({ disabled = false, children }) => {
const buttonRef = useRef(null);
const [tabIndex, focused, handleKeyDown, handleClick] = useRover(buttonRef, disabled);
useFocus(focused, buttonRef);
function onKeyDown(event) {
handleKeyDown(event);
yourOwnFunctionToDoWhatever(event);
}
function onClick(event) {
handleClick(event);
yourOwnFunctionToDoWhatever(event);
}
return (
<button
ref={ref}
type="button"
tabIndex={tabIndex}
disabled={disabled}
onKeyDown={handleKeyDown}
onClick={onClick}
>
{children}
</button>
);
};
3. Place them inside your RoverProvider structure
Since RovingTabIndex relies on React Context, there's no need to have the buttons all as direct children of the provider.
You can nest as deep as you'd like and it will work as well 🙂
<RoverProvider direction="horizontal">
<MenuButton>First Button</MenuButton>
<MenuButton disabled>Second Button</MenuButton>
<ul>
<li><MenuButton>Another Button</MenuButton>
<li><MenuButton>Another Button</MenuButton>
<li><MenuButton>Another Button</MenuButton>
<li><MenuButton>Another Button</MenuButton>
</ul>
</RoverProvider>
Differences with Focus Manager
Both are made to deal with focus and navigation but do it in different ways and for different scenarios:
-
The
FocusManagercan, if necessary, scope the focus inside a group and allows the user to press thetabkey to move to the next "tabbable" element (orshift+tabto move to the previous).💡 Tip: Use it for dealing with interface elements that require focus control, like popovers, modals and side menus.
-
The
RoverProvidercan also manage focus inside a group but, instead of using thetabkey to move back and forth between elements, it uses navigation keys (ArrowUp,ArrowDown,ArrowLeftorArrowRight) to do that.It also makes the other group's elements unable to receive focus using the
tabkey since it applies the negativetabindexto the components that are not currently selected.💡 Tip: Use it for dealing with interface elements that require selection, like a custom select element or a navigation menu. (Still, you should try to use the native HTML elements).