RoverProvider
An accessibility pattern for a grouped set of inputs
- Use the tab key to navigate to the Menu.
- Then, use the
ArrowUp
andArrowDown
keys to go through each option. - Try pressing
Home
orEnd
to 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"Home
key to go to the first elementEnd
key 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
FocusManager
can, if necessary, scope the focus inside a group and allows the user to press thetab
key to move to the next "tabbable" element (orshift+tab
to move to the previous).💡 Tip: Use it for dealing with interface elements that require focus control, like popovers, modals and side menus.
-
The
RoverProvider
can also manage focus inside a group but, instead of using thetab
key to move back and forth between elements, it uses navigation keys (ArrowUp
,ArrowDown
,ArrowLeft
orArrowRight
) to do that.It also makes the other group's elements unable to receive focus using the
tab
key since it applies the negativetabindex
to 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).