Skip to content
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

refactor(v2): remove focus outline from mouse users #3626

Merged
merged 4 commits into from
Oct 27, 2020

Conversation

lex111
Copy link
Contributor

@lex111 lex111 commented Oct 22, 2020

Motivation

We currently have outline displayed after clicking on the dropdown item, similar to tabs (which was decided in #3171).

Therefore, in order to avoid duplicate code, we are better off using more general solution for the whole application than applying this fix (remove outline when not tab) to proper components.

This a11y issue is detailed in this blog post https://hackernoon.com/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2 that I was inspired in this PR.

Have you read the Contributing Guidelines on pull requests?

Yes

Test Plan

Preview.

Related PRs

(If this PR adds or changes functionality, please take some time to update the docs at https://github.com/facebook/docusaurus, and link to your PR here.)

@lex111 lex111 added the pr: polish This PR adds a very minor behavior improvement that users will enjoy. label Oct 22, 2020
@lex111 lex111 requested a review from slorber as a code owner October 22, 2020 23:26
@facebook-github-bot facebook-github-bot added the CLA Signed Signed Facebook CLA label Oct 22, 2020
if (e.key === 'Tab') {
document.body.classList.add(keyboardFocusedClassName);

document.removeEventListener('keydown', handleFirstTab);
Copy link
Contributor Author

@lex111 lex111 Oct 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For best performance, I add/remove event handlers depending on whether the focus indicator is enabled. Is it worth it? Or we'd better add event handlers once when the Layout component is mounted (as it was done before in Tabs component), and accordingly remove them only when the component is unmounted (from useEffect return).

Something like this:

  useEffect(() => {
    if (!ExecutionEnvironment.canUseDOM) {
      return undefined;
    }

    const keyboardFocusedClassName = 'navigation-with-keyboard';

    function handleTab(e: MouseEvent | KeyboardEvent) {
      document.body.classList.toggle(keyboardFocusedClassName, e.key === 'Tab');
    }


    document.addEventListener('mousedown', handleTab);
    document.addEventListener('keydown', handleTab);

    return () => {
      document.body.classList.remove(keyboardFocusedClassName);
      document.removeEventListener('keydown', handleTab);
      document.removeEventListener('mousedown', handleTab);
    };
  }, []);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless we have a performance issue for tabs, which is unlikely, I'd rather keep the code simpler to read/understand.

But I'm not sure the classList.toggle would work great because it would remove the outline on non-tab key presses? A user might navigate to inputs with keyboard, and then type in the input, and I think the focus ring should remain in such case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the outline will be displayed, you can see this on the page https://deploy-preview-3626--docusaurus-2.netlify.app/classic/docs/styling-layout

image

@docusaurus-bot
Copy link
Contributor

docusaurus-bot commented Oct 22, 2020

Deploy preview for docusaurus-2 ready!

Built with commit 4d5b422

https://deploy-preview-3626--docusaurus-2.netlify.app

document.body.classList.remove(keyboardFocusedClassName);

document.removeEventListener('keydown', handleFirstTab);
document.removeEventListener('mousedown', handleMouseDown);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use mousemove instead of mousedown event? Not sure about that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure either, maybe the user is tabbing and moving the mouse at the same time 🤪 probably a niche usecase not very important to consider

Copy link
Collaborator

@slorber slorber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems pretty good.

I started separating theme/style components from behavioral ones, because we want at some point to support multiple themes (classic/bootstrap/tailwind...), all having the same UX (just the style and css system would change).

This code is likely to be shared between the 3 themes, and not meant to be swizzled, so I'd rather move it to src/client, until we create a new package with all the design-agnostic theme code. (there's outline none, but I think this still make sense for all themes to adopt this behavior)

document.body.classList.remove(keyboardFocusedClassName);

document.removeEventListener('keydown', handleFirstTab);
document.removeEventListener('mousedown', handleMouseDown);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure either, maybe the user is tabbing and moving the mouse at the same time 🤪 probably a niche usecase not very important to consider

useEffect(() => {
if (!ExecutionEnvironment.canUseDOM) {
return undefined;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not needed, useEffect never runs in node

if (e.key === 'Tab') {
document.body.classList.add(keyboardFocusedClassName);

document.removeEventListener('keydown', handleFirstTab);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless we have a performance issue for tabs, which is unlikely, I'd rather keep the code simpler to read/understand.

But I'm not sure the classList.toggle would work great because it would remove the outline on non-tab key presses? A user might navigate to inputs with keyboard, and then type in the input, and I think the focus ring should remain in such case?

@lex111
Copy link
Contributor Author

lex111 commented Oct 26, 2020

This code is likely to be shared between the 3 themes, and not meant to be swizzled, so I'd rather move it to src/client, until we create a new package with all the design-agnostic theme code. (there's outline none, but I think this still make sense for all themes to adopt this behavior)

This must be done in a separate PR. Moreover, I did not delete anything in the Bootstrap theme, so focus behavior will is the same as before.

@slorber
Copy link
Collaborator

slorber commented Oct 27, 2020

Hey @lex111, found this potential new solution that looks more future proof.

Do you think we should adopt it instead?

https://matthiasott.com/notes/focus-visible-is-here

@lex111
Copy link
Contributor Author

lex111 commented Oct 27, 2020

It's cool, but Safari and Opera don't support it. I think it's better to wait until global support is at least 50% https://caniuse.com/?search=focus-visible We also have tablets that are connected to keyboard (for example, iPad with Magic Keyboard). WDYT?

Copy link
Collaborator

@slorber slorber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, feel free to self merge if ready on your side.

We can improve this later with :focus-visible when browser support will be better, and refactor to a separate package when we start working on the 3 themes

Was wondering if it's worth adding a touchStart listener or something for touch devices with keyboards that you mentioned? Not sure it's needed.

@lex111 lex111 merged commit 9837299 into master Oct 27, 2020
@lex111 lex111 deleted the lex111/graceful-focus-outline branch November 6, 2020 11:14
@lex111 lex111 added this to the v2.0.0-alpha.67 milestone Nov 17, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed Signed Facebook CLA pr: polish This PR adds a very minor behavior improvement that users will enjoy.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants