-
Notifications
You must be signed in to change notification settings - Fork 384
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement keyboard and focus management #3897
Conversation
Currently it's only in use for the tabs in the Library. If you focus any tab in the library, pressing tab and shift-tab will move correctly back and forwards between the tabs and pressing enter will switch selection to the focused tab. However, pressing left and right arrows will also navigate to the other tabs (even observing reading direction), but of course not outside the tab list. This should be fully accessible and semantic..
Current early implementation only works for tabs: Note that you can use the mouse to navigate and focus, you can use tab and shift+tab to focus and then enter to select, but you can also use the arrow keys, left and right (observing reading direction). Using tab allows you to of course tab to other sections of the application, whereas using arrow keys only let you navigate between the tabs and not any further. |
Page carousel now also uses navigable and navigablegroup - and you can press delete to delete the currently focused page.
|
||
&:hover { | ||
background-color: ${ ( { theme } ) => theme.colors.fg.v1 }; | ||
} | ||
|
||
&:focus, &:active { | ||
outline: none; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
General believe, i think, is that outline
s are good for accessibility. Is there any reason you don't like it here? I think replacing it with border is fine, but sometimes there are other factors. For instance, if UX asks to add an extra pixel to the outline, very little needs to change, vs making border
thicker could cause relayout. But other than that, this is not critical.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm am open to using either outline
or border
, no preference. However, the UX called for a specific design with a specific border and I wasn't able to replicate it using outline
only. For a11y purposes I think we're safe as long as we do something else to provide highlight.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you point me to the precise place in figma with that style. I just want to confirm it once so we don't have to stumble on this each time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. We'd just need to always reserve a border and only manipulate the border-color
from transparent
to focused color.
const setRef = useCombinedRefs( ref, forwardedRef ); | ||
|
||
useLayoutEffect( () => { | ||
const mt = MouseTrap( ref.current ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to clarify: this shouldn't be new MouseTrap
, should it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mousetrap doesn't use new
in their docs, but I do see that WP does in their implementation, that I was inspired by.
I feel that I remember seeing something about new
syntax being discouraged, but I can't find it again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docs say:
You can also create a new Mousetrap instance if you want to bind a bunch of things to the same element.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I don't really know what the difference is here (that is, the actual effect of doing either). The current implementation works, but maybe not efficiently?
|
||
function Focusable( { shortcuts, hasFocus, isGlobal, element, className, role, tabindex, forwardedRef, children, ...rest } ) { | ||
const ref = useRef(); | ||
const setRef = useCombinedRefs( ref, forwardedRef ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just use React.forwardRef
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should also use forwardRef, yes (which allows me to use ref={}
in the parent component), but I still need some way to merge the two refs, when needing the ref both in the component itself and in a parent component. It can't be done any other way, unfortunately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A single forwarded ref usually does the trick for me, but sure, if there's need, let's keep.
const focus = ( node ) => node && node.focus(); | ||
|
||
if ( direction === NavigableGroup.DIRECTION_HORIZONTAL ) { | ||
const { forward, backward } = getDirectionalKeysForNode( ref.current ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes sense, but to clarify: what does Tab
do inside the NavigableGroup?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pressing tab navigates between focusable elements using browser-native implementation. Just like enter clicks the currently focused element - this is also using browser-native behaviour. I believe that's excellent for a11y.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. I think that's good. In some systems tab jumps between bigger blocks and arrows are used to navigate within the block. But I think for now a simple way is better. This might pop up for e.g. gallery, where there might be unlimited set of things to tab over, which could make tab harder to use to navigate to other elements.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the tablist implementation at https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-1/tabs.html and the one I built for WordPress core (click on share icon on https://wordpress.org/news/2019/12/wordpress-5-3-1-security-and-maintenance-release/embed/), pressing tab jumps to the current tab's content, not to the next tab.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the tablist implementation at https://www.w3.org/TR/wai-aria-practices/examples/tabs/tabs-1/tabs.html and the one I built for WordPress core (click on share icon on https://wordpress.org/news/2019/12/wordpress-5-3-1-security-and-maintenance-release/embed/), pressing tab jumps to the current tab's content, not to the next tab.
Thanks a lot for those links - very helpful!
Those definitely are different ways of implementing this. The inactive tabs don't participate in tab order at all it seems (tabindex="-1"
) - only the active tab and the tab content. tab and shift+tab then navigate between the active tab and the tab content in the regular way based off document order.
And they have an example of both automatic and manual tab switching. Very interesting approaches - I will read up on those!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few comments, but otherwise LG
const Tabs = styled( NavigableGroup ).attrs( { | ||
element: 'nav', | ||
role: 'tablist', | ||
tabindex: -1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the default focus CSS (outline) also be removed here? (Since it can still focus by clicking)
This PR is over a month out of date. So can merge for |
Summary
This implements proper semantic and accessible keyboard and focus management.
This is work in progress. Please see individual commit messages for details.
Fixes #3826.
Checklist