Skip to content

[UEPR-483] "Choose a Sprite" and "Choose a Backdrop" keyboard accessibility#433

Merged
KManolov3 merged 42 commits intoscratchfoundation:release/UEPR-297-accessibility-improvementsfrom
kbangelov:task/uepr-483-choose-a-sprite-and-backdrop-accessibility
Mar 12, 2026
Merged

[UEPR-483] "Choose a Sprite" and "Choose a Backdrop" keyboard accessibility#433
KManolov3 merged 42 commits intoscratchfoundation:release/UEPR-297-accessibility-improvementsfrom
kbangelov:task/uepr-483-choose-a-sprite-and-backdrop-accessibility

Conversation

@kbangelov
Copy link
Contributor

@kbangelov kbangelov commented Feb 3, 2026

Resolves

https://scratchfoundation.atlassian.net/browse/UEPR-483

Proposed Changes

  • Adds keyboard navigation logic.
  • UI bug: tooltips don't refresh in case of first open or shift+tab-ing back to the menu.

Reason for Changes

Part of accessibility initiative for Scratch.

@kbangelov kbangelov requested a review from a team as a code owner February 3, 2026 14:18
@adzhindzhi adzhindzhi changed the base branch from develop to release/accessibility-improvements February 6, 2026 09:27
@kbangelov kbangelov force-pushed the release/accessibility-improvements branch 2 times, most recently from e37597f to f2902ce Compare February 13, 2026 09:00
@kbangelov kbangelov changed the base branch from release/accessibility-improvements to release/UEPR-297-accessibility-improvements February 13, 2026 10:10
@kbangelov kbangelov requested a review from adzhindzhi February 16, 2026 11:20
@kbangelov kbangelov requested a review from adzhindzhi February 17, 2026 11:00
@kbangelov kbangelov requested a review from adzhindzhi February 18, 2026 10:55
@kbangelov
Copy link
Contributor Author

kbangelov commented Feb 26, 2026

The latest commit fixes the following 3 bugs that existed:

  • Escape used to not show the tooltip for the core element
  • if we selected a sprite from inside the menu, it showed a double tooltip on the way back
  • clicking escape on the tooltip selector library used to also close the action menu on the way back

What I did was remove onBlur logic and replace it in the following way - onBlur unites a few of the following cases:

  • clicking somewhere else (mousedown works now, unlike touchstart)
  • we were also already handling tab
  • the one blur motion we don't handle because we don't want to is after clicking on a menu option that takes us to library or file upload, where we now don't close the menu, which avoids most (hopefully all) bugs.

@kbangelov kbangelov requested a review from adzhindzhi February 26, 2026 08:42
Copy link
Contributor

@KManolov3 KManolov3 left a comment

Choose a reason for hiding this comment

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

Left a few minor comments, but otherwise the behavior looks very smooth to me now! 🙌
There's a lot of logic "under the hood" which may end up being finicky, but hopefully this doesn't end up getting changed very often.

} else if (e.key === KEY.TAB) {
setIsExpanded(false);
focusItem(buttonRef.current);
} else if (e.key === KEY.ESCAPE) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I see some possibly unintended behavior when clicking ESCAPE, after which attempting to TAB- I need to TAB twice for something to happen. It's more of a corner case, but might be worth fixing if it isn't too hard. Otherwise I am fine with leaving as is for now.

Copy link
Contributor Author

@kbangelov kbangelov Feb 27, 2026

Choose a reason for hiding this comment

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

Hmm, it seems to be another issue that appears on firefox but not chrome. Upon reconsideration, I am not sure if Escape behavior is something we want at all here. I mean there are other intuitive ways to focus away as a keyboard or mouse user, such as tab or clicking outside of the menu. What do you think?

There is the "we want to be able to focus the core option" argument but the bottom one already duplicates that behavior on every use of action menu.

A compromise would be "focus on the main button on escape but keep the menu open, since we can still enter it with arrows". Collapsing seems a little unnatural to me now.

}), []
);

const handleItemMouseEnter = useCallback(index => () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you leave a comment why this is needed?

@kbangelov kbangelov requested a review from KManolov3 February 27, 2026 09:00
@@ -222,12 +196,10 @@ const ActionMenu = ({

const handleClick = useCallback(e => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Having a useCallback inside of a map is an antipattern - https://react.dev/reference/rules/rules-of-hooks#only-call-hooks-at-the-top-level. We should be able to extract this on top level.

@kbangelov kbangelov requested a review from KManolov3 March 10, 2026 11:46
Copy link
Contributor

@adzhindzhi adzhindzhi left a comment

Choose a reason for hiding this comment

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

Looks good to me! 🙌

Comment on lines +36 to +40
setTimeout(() => {
const focusedElement = document.activeElement;
focusedElement.blur();
focusItem(focusedElement);
}, CLOSE_DELAY * 1.5);
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: We could probably extract this into a function, since we use the same logic twice?

const focusedElement = document.activeElement;
focusedElement.blur();
focusItem(focusedElement);
}, CLOSE_DELAY * 1.5);
Copy link
Contributor

Choose a reason for hiding this comment

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

1.5 is a magic constant. Why 1.5 exactly - can we add a comment?

}, CLOSE_DELAY * 1.5);
}, []);

useEffect(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we add comments in what usecases the different useEffects will be relevant

@kbangelov kbangelov requested a review from KManolov3 March 12, 2026 10:38
focusItem(focusedElement);
}, CLOSE_DELAY * 1.5);
}, []);
}, 500);
Copy link
Contributor

Choose a reason for hiding this comment

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

500 is also a magic constant 😅 I think it was a bit better when it was connected with the CLOSE_DELAY. But we can update this later.

@KManolov3 KManolov3 merged commit 92fedf5 into scratchfoundation:release/UEPR-297-accessibility-improvements Mar 12, 2026
6 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Mar 12, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants