-
-
Notifications
You must be signed in to change notification settings - Fork 795
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
Dialog close button comes before dialog contents in tab order #1567
Comments
We don't want to explicitly set tab orders in dialogs, so maybe removing the close button from the tab order is the way to go. Any thoughts on this, @KonnorRogers? |
I don't particularly love omitting the close key from the keyboard order...if anything this feels like a bug in our focus trapping. I'll take a look in the morning and see if I can debug what's happening. |
@KonnorRogers we can't use |
@claviska would |
Unfortunately no. That will also prevent clicks. 😢 |
Was this avenue explored any further? I tend to agree that omitting the close button entirely from tab order isn't ideal, and I don't actually understand how the current behaviour is happening. I'd assume the DOM tree for the dialog itself is something like this: <div part="base">
<div part="header">
<!-- dialog label slot -->
<!-- dialog header actions slot -->
<sl-icon-button name="x"></sl-icon-button><!-- close button -->
</div>
<div part="panel"><!-- this element is focused on open -->
<!-- anonymous slot -->
</div>
<div class="dialog-footer">
<!-- buttons etc. -->
</div>
</div> To me it's very mysterious that when the |
@grncdr hmmm....this may have to do with our focus trapping code not realizing that a trapped element has already been focused. I think I may have a fix for this. |
So there's 2 parts to this problem.
<div part="panel">
<header part="header" class="dialog__header">
<h2 part="title" class="dialog__title" id="title">
</h2>
<div part="header-actions" class="dialog__header-actions">
<slot name="header-actions"></slot>
<sl-icon-button
part="close-button"
></sl-icon-button>
</div>
</header>
<slot part="body" class="dialog__body" tabindex="-1"></slot>
<footer part="footer" class="dialog__footer">
<slot name="footer"></slot>
</footer>
</div> So we initially focus the But I managed to solve the first part. You could also throw <sl-dialog label="Dialog" class="dialog-overview">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
<sl-input autofocus placeholder="tab to me"></sl-input>
<sl-button slot="footer" variant="primary">Close</sl-button>
</sl-dialog> @claviska I guess the only pause would be if you have any other focusable elements in that header area. |
Dialogs and drawers both support header actions that should be tabbable. https://shoelace.style/components/dialog/#header-actions
I'm not sure that's a good idea. The initial focus moves it to this element, which has the Moving it to the body will be confusing to screen reader users, as it won't be announced as a dialog. |
Yes, I'd considered that and so far it works for any case I need it to.
I am also using header actions in a few cases, and I (very subjectively) would expect them to also come after dialog content in tab order. Idea 1What about re-ordering the DOM and then visually placing the header actions at the top of the dialog? (e.g. with CSS grid areas) <div part="panel" role="dialog" ...>
<header part="header" class="dialog__header">
<h2 part="title" class="dialog__title" id="title"></h2>
</header>
<slot part="body" class="dialog__body" tabindex="-1"></slot>
<footer part="footer" class="dialog__footer">
<slot name="footer"></slot>
</footer>
<div part="header-actions" class="dialog__header-actions">
<slot name="header-actions"></slot>
<sl-icon-button
part="close-button"
></sl-icon-button>
</div>
</div> Then you get the "expected" (by me, at least... 😅) tab order without having to mess with tabindex or (hopefully) the experience of screen reader users. Idea 2The dialog could also implement the same accessibility pattern you'd use for an entire page: inserting an invisible "Skip to content" link at the start of the dialog header. <div part="panel" role="dialog" ...>
<header part="header" class="dialog__header">
<a onClick="..."><slot name="skip-to-content">Skip to content</slot></a>
<h2 part="title" class="dialog__title" id="title"></h2>
<div part="header-actions" class="dialog__header-actions">
<slot name="header-actions"></slot>
<sl-icon-button
part="close-button"
></sl-icon-button>
</div>
</header>
<slot part="body" class="dialog__body" tabindex="-1"></slot>
<footer part="footer" class="dialog__footer">
<slot name="footer"></slot>
</footer>
</div> This would be pretty robust, but also maybe still irritating, as it essentially replaces |
This is a great idea, especially considering the containing panel is already using If we use this approach, we'll need to separate header actions from the header. I'm not sure how feasible that is.
In most cases, when the close button is the only header action, this will require the same number of interactions to get to the content. I don't see a real benefit here, except if you quickly tab and hit Enter it wouldn't close. However, that sort of muscle memory probably won't exist unless you use the dialog a lot, in which case users will have learned that doing so closes the dialog. I decided to look at some other examples to see how they're handling it.
I appreciate why you're asking for this, but it seems like it's coming down to personal preference. I can't find anything in the APG that suggest this is an antipattern. Note that you can also use the https://codepen.io/claviska/pen/abPKBWX I'm leaning towards this being a wontfix, because so many other libraries behave the same way. While I see your point of view, many other libraries do the same thing and there's no obvious solution unfolding here. If it's bothersome to you, it's probably best to use |
That's totally reasonable. After looking at all the examples you provided, I will probably keep the default behaviour. 😅
Edit: just saw that you already approved that PR. I think this can be closed. |
Describe the bug
When a dialog is opened, the dialog panel is focused (if no element in the dialog has an autofocus attribute).
However, hitting
Tab
does not focus the first focusable element inside the dialog panel, but instead it focuses the close button.To Reproduce
Steps to reproduce the behavior:
Tab
sl-input
.Demo
https://codepen.io/grncdr/pen/zYydxoq?editors=1000
Browser / OS
Additional information
I suspect this might be intentional? If so, consider this a feature request.
I would expect the focus order to be:
Such that
Shift+Tab
would focus the close button first.If that's too strange/difficult, I'd also be happy to see an option that allows omitting the
X
from the focus order all together, sinceEsc
does the same thing for users that wish to navigate with the keyboard.The text was updated successfully, but these errors were encountered: