Re-focus dropdown button before closing menu with keyboard #40

Merged
marijn merged 1 commit from singingwolfboy/prosemirror-menu:dropdown-focus-before-close into main 2026-04-14 11:58:20 +02:00
Contributor

We are using this code in our project -- thanks so much for sharing it! I've found an interesting accessibility bug where closing a dropdown menu using the keyboard causes the focus to move to the root of the document, rather than returning to the dropdown button. It appears to be because we are using CSS to hide the menu when the editor is not focused, using CSS rules like these:

.ProseMirror-menubar {
    display: none;
}
.ProseMirror-menubar-wrapper:focus-within .ProseMirror-menubar {
    display: flex;
}

When the existing code closes the dropdown menu, the focused element is removed from the DOM, which causes the focus to move to the root of the document. This causes the display: none to apply, because the focus is no longer within the menubar. Then, the JS tries to move the focus to the button in the menubar, but it can't, because the browser won't move focus to an element that has display: none.

Switching the order of these statements appears to make it work as expected. In most cases, it shouldn't matter in what order these statements execute; do you mind making the btn.focus() line run first, so that it properly handles this edge case?

We are using this code in our project -- thanks so much for sharing it! I've found an interesting accessibility bug where closing a dropdown menu using the keyboard causes the focus to move to the root of the document, rather than returning to the dropdown button. It appears to be because we are using CSS to hide the menu when the editor is not focused, using CSS rules like these: ```css .ProseMirror-menubar { display: none; } .ProseMirror-menubar-wrapper:focus-within .ProseMirror-menubar { display: flex; } ``` When the existing code closes the dropdown menu, the focused element is removed from the DOM, which causes the focus to move to the root of the document. This causes the `display: none` to apply, because the focus is no longer within the menubar. Then, the JS tries to move the focus to the button in the menubar, but it can't, because the browser won't move focus to an element that has `display: none`. Switching the order of these statements appears to make it work as expected. In most cases, it shouldn't matter in what order these statements execute; do you mind making the `btn.focus()` line run first, so that it properly handles this edge case?
marijn merged commit daba5c859d into main 2026-04-14 11:58:20 +02:00
Owner

Thanks! This looks like a good idea.

Thanks! This looks like a good idea.
Owner

I've tagged this in prosemirror-menu 1.3.1

I've tagged this in prosemirror-menu 1.3.1
Author
Contributor

Thank you!

Thank you!
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
prosemirror/prosemirror-menu!40
No description provided.