Skip to content

Parsons accessibility - #1453

Open
ascholerChemeketa wants to merge 6 commits into
RunestoneInteractive:mainfrom
ascholerChemeketa:parsons-accessibility
Open

Parsons accessibility#1453
ascholerChemeketa wants to merge 6 commits into
RunestoneInteractive:mainfrom
ascholerChemeketa:parsons-accessibility

Conversation

@ascholerChemeketa

Copy link
Copy Markdown
Contributor

Fixes some accessibility issues with Parsons.

  • Old keyboard navigation was incompatible with screen readers.
  • Extracts mathjax generated strings to use for accessible text in blocks.
  • Replaces js popups communicating help changes with updates to the message area to avoid needlessly grabbing focus.
  • Adds aria information about block position and status (incorrect or not) to labels for blocks.

Note: Keyboard navigation now requires activation - use tab to focus on the block area and then hit enter.


This pull request introduces several accessibility and keyboard navigation improvements to the Parsons problem interactive, along with related visual and internationalization updates. The most significant changes enhance the keyboard navigation experience for users, provide clearer visual cues during keyboard interactions, and add new localized messages. The changes also refactor event handling and focus management to better support these features.

Accessibility and Keyboard Navigation Improvements:

  • Added role="button" to each Parsons block and improved keyboard event handling, allowing users to activate and move blocks using the keyboard. Focus management and event listeners were refactored to support keyboard navigation and movement modes. (parsonsBlock.js) [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] [12] [13] [14] [15] [16] [17] [18] [19] [20] [21]

  • Added a new absolutely positioned, pointer-events-disabled container (.parsons-keyboard-application) to the Parsons widget for improved keyboard navigation overlays. (parsons.css)

Visual Feedback Enhancements:

  • Added solid and dashed outlines to .block.down and .block.up states, providing clear visual cues when a block is selected or being moved via keyboard. (parsons.css)

Internationalization:

  • Added the message for keyboard activation ("Enter to activate") in English, Brazilian Portuguese, and Serbian (Cyrillic) localization files. (parsons-i18n.en.js, parsons-i18n.pt-br.js, parsons-i18n.sr-Cyrl.js) [1] [2] [3]

These changes collectively make the Parsons problem interactive more accessible and user-friendly, especially for keyboard users.

Copilot AI lite review requested due to automatic review settings August 31, 2026 16:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Disabled blocks can skip click-handler initialization, leading to a potential runtime error when removing the click listener during disable/destroy flows.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR improves accessibility and keyboard navigation for the Runestone Parsons interactive by shifting keyboard entry to the sortable container, enhancing screen-reader announcements (including MathJax speech extraction), replacing alert-based help messaging with live-region updates, and adding visual focus/move cues.

Changes:

  • Refactors keyboard interaction to require explicit activation (Enter/Space) and centralizes key handling in the Parsons container.
  • Adds accessible labels/announcements for blocks (including MathJax speech text) and improves live-region messaging for adaptive help.
  • Updates styling for keyboard-selection states and adds localized text for the new keyboard activation hint.
File summaries
File Description
bases/rsptx/interactives/runestone/parsons/test/parsons.test.js Updates/adds tests for the new keyboard activation model, aria-label behavior, and live-region help messaging.
bases/rsptx/interactives/runestone/parsons/js/parsonsBlock.js Adds ARIA role on blocks and refactors focus/click behavior for keyboard interaction.
bases/rsptx/interactives/runestone/parsons/js/parsons.js Implements the new keyboard activation flow, live announcements, MathJax speech observation, and message handling refactor.
bases/rsptx/interactives/runestone/parsons/js/parsons-i18n.en.js Adds localized “Enter to activate” message key.
bases/rsptx/interactives/runestone/parsons/js/parsons-i18n.pt-br.js Adds localized “Enter to activate” message key.
bases/rsptx/interactives/runestone/parsons/js/parsons-i18n.sr-Cyrl.js Adds localized “Enter to activate” message key.
bases/rsptx/interactives/runestone/parsons/css/parsons.css Adds keyboard overlay container styling and new outlines for selected/moving block states.
Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread bases/rsptx/interactives/runestone/parsons/js/parsons.js
Comment thread bases/rsptx/interactives/runestone/parsons/js/parsonsBlock.js Outdated
Copilot AI review requested due to automatic review settings August 31, 2026 17:11
@ascholerChemeketa

Copy link
Copy Markdown
Contributor Author

Updated to address issues.

Note that translations for other languages are AI generated. They look OK based on a quick check with google translate, but might need fine tuning.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Several newly introduced keyboard/a11y paths can break or behave incorrectly in real usage (selector support exception risk, stale live-region updates, and focused-block disable/destroy cleanup).

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (4)

Previously missed (2) — in code that hasn't changed since the last review.

bases/rsptx/interactives/runestone/parsons/js/parsons.js:197

  • getBlockLabel derives the accessible label from the entire block container, which can include non-code decorations (e.g., left/right numbering labels) and make screen readers announce extra text. Use the .lines element as the source for accessible text so only the block content (and MathJax speech) is used.
    getBlockLabel(block) {
        return getAccessibleElementText(block.view) || t("msg_parson_block");
    }

bases/rsptx/interactives/runestone/parsons/js/parsons.js:3221

  • showMessage updates messageDiv asynchronously via setTimeout, but clearFeedback doesn't invalidate pending updates. If clearFeedback() runs within the 10ms window, the delayed callback can repopulate the live region after it's been cleared/hidden, leading to stale announcements. Bump messageVersion and clear the message contents when clearing feedback.
        this.messageDiv.style.visibility = "hidden";
        this.pendingHelpMessage = undefined;
        this.hideBlockExplanations();
        this.updateBlockAriaLabels();

bases/rsptx/interactives/runestone/parsons/js/parsonsBlock.js:444

  • disable() still checks for tabindex == "0", but blocks are no longer assigned tabindex="0" (the sortable container is now the entry point). This makes the focused-block cleanup path unreachable and can leave textFocus/keyboard state pointing at a disabled block. Use this.problem.textFocus === this to decide whether to cancel keyboard mode when disabling.

This issue also appears on line 466 of the same file.

        this.view.removeEventListener("click", this.clickHandler);
        if (this.view.getAttribute("tabindex") == "0") {
            this.releaseFocus();
            this.view.removeAttribute("tabindex");
            this.problem.initializeTabIndex();

bases/rsptx/interactives/runestone/parsons/js/parsonsBlock.js:470

  • destroy() also checks for tabindex == "0", which is now unreachable. If the destroyed block is the current textFocus, the component can retain stale keyboard state referencing a removed element. Cancel keyboard mode when destroying the focused block.
        this.view.removeEventListener("click", this.clickHandler);
        if (this.view.getAttribute("tabindex") == "0") {
            this.releaseFocus();
        }
        this.view.removeAttribute("tabindex");
  • Files reviewed: 7/7 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread bases/rsptx/interactives/runestone/parsons/js/parsons.js
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

Successfully merging this pull request may close these issues.

2 participants