Skip to content

[BUG]: Dynamic directive fails to trap keyboard at first #6

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

Closed
J-Sek opened this issue Sep 26, 2023 · 9 comments
Closed

[BUG]: Dynamic directive fails to trap keyboard at first #6

J-Sek opened this issue Sep 26, 2023 · 9 comments

Comments

@J-Sek
Copy link

J-Sek commented Sep 26, 2023

Describe the bug
I tried to use dynamic directive, but it works partially. First round of tabbing escapes the trap. When I go back inside (e.g. using [SHIFT] + [TAB]), the trap works.

<dialog v-kbd-trap='visible'>
  <!-- content -->
</dialog>

Example on CodeSandbox

To Reproduce
Steps to reproduce the behavior:

  1. Use v-kbd-trap="enabled" on a rendered element
  2. Open a dialog (should change enabled to true)
  3. Click [TAB] few times
    • This will focus elements outside of the dialog
  4. Click [SHIFT] + [TAB] to get back into the dialog
  5. Click [TAB] few times again
    • This will keep focus inside the dialog

Expected behavior
The directive should keep focus inside all the time

Recording
Peek 2023-09-26 13-38

Environment:

  • OS: Ubuntu 20.04
  • Browser: Chrome, Edge, Firefox (latest versions)

Workaround
Additional <div> with v-if... so actually it means you cannot use "dynamic" mode at all.

<dialog>
  <div v-if='visible' v-kbd-trap>
    <!-- content -->
  <div>
</dialog>
@pdanpdan
Copy link
Owner

Thank you for reporting. Should be fixed with v1.0.18 on npm

@J-Sek
Copy link
Author

J-Sek commented Sep 27, 2023

Thanks. I can see the newest version prevents focus outside of dialog, but the outline is missing on links (in my CodeSandbox example) for some reason. It also disappears when I click on one of the links... looks more like a browser issue. What precisely happens is :focus-visible is not applied. Elements only get :focus.

However I can mitigate it by using :inert="!visible" next to the v-kdb-trap='visible'.

<dialog ref="dialog" :inert="!visible" v-kbd-trap="visible">
  <!-- content -->
</dialog>

@pdanpdan
Copy link
Owner

pdanpdan commented Sep 27, 2023

Same codesandbox url?

Hmm, it's strange.

  • if I add a tabindex="0" to a link or if I set specific classes for focus-visible they work
  • also looks like it's chrome-related (chrome and edge show the problem, firefox does not)
  • in chrome if I open the dialog by keyboard than all outlines are visible as expected
    • if then I click inside the dialog and later use keyboard to navigate the last link has no outline

https://codesandbox.io/p/sandbox/vue-keyboard-trap-issue-forked-93sqt9?file=%2Fsrc%2Fcomponents%2FDialogExample.vue%3A9%2C33

If you discover something (or have any idea) please tell me.

@J-Sek
Copy link
Author

J-Sek commented Sep 27, 2023

After reading spec I suspect that Chrome is more in-line with the requirements:

If the previously-focused element indicated focus, and a script causes focus to move elsewhere, the newly focused element should indicate focus.

Conversely, if the previously-focused element did not indicate focus, and a script causes focus to move elsewhere, the newly focused element should also not indicate focus.

  • Mousedown focuses button with regular :focus and opens dialog (programatically)
  • When opened :focus shifts now to the first link on the list
  • Tabbing continues to keep :focus because it is actually intercepted by v-kdb-trap so every next selection is considered to be done via "a script"

The button and inputs behave different than links... maybe because of heuristics mentioned by the spec.

Your helper function calling .focus() could make use of focusVisible option once stops being experimental. Current coverage is quite poor.

Overall I can live with 2 workarounds (inert and additional <div v-if>). No need to stress about it. Thanks to your timely responses I digged more and learned something new 😄 And the focus is trapped inside the dialog, so the issue was successfully resolved.

@pdanpdan
Copy link
Owner

I would agree with them if they were consistent.
But if the link has no style for focus-visible and no tabindex then the outline is not shown, while any of the 2 is present the indicator is shown.

@J-Sek
Copy link
Author

J-Sek commented Sep 27, 2023

I think you mean a:focus has no outline, hence is not visible in the example. a:focus-visible does not require any styles as it gets 1px outline from the built-in browser stylesheet.

@pdanpdan
Copy link
Owner

No, I mean that:

  • <a href="#3"> does not show the focus ring
  • <a href="#2" tabindex="0"> shows the ring (if it has tabindex it shows the ring)
  • <a href="#1" class="focus-visible:ring"> shows the ring (focus-visible:ring adds a rule like something:focus-visible which should be like the default one)
  • if you add a generic CSS rule like :focus-visible { outline-style: solid; outline-color: currentColor; } the the indicator is shown on all links (it looks like by default the ring is shown but using a wrong default style and default color)

@J-Sek
Copy link
Author

J-Sek commented Sep 28, 2023

Oh... I see. The styles for :focus-visible for <a> tell Chrome's heuristics to add focus-visible state in situations when it would normally only use only focus.

The generic CSS rule should be a decent way to force some consistency across the browsers, for now.

@pdanpdan
Copy link
Owner

You should upgrade to v1.0.19 - a fix not to add tabindex to dialog
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog#attributes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants