Skip to content

fix: improve keyboard and screenreader accessibility for Video Shorts#3387

Open
daniellefrappier18 wants to merge 14 commits into
mainfrom
daniellef/11527-video-shorts-a11y-fix
Open

fix: improve keyboard and screenreader accessibility for Video Shorts#3387
daniellefrappier18 wants to merge 14 commits into
mainfrom
daniellef/11527-video-shorts-a11y-fix

Conversation

@daniellefrappier18
Copy link
Copy Markdown
Contributor

@daniellefrappier18 daniellefrappier18 commented May 29, 2026

What are the relevant tickets?

Fixes https://github.com/mitodl/hq/issues/11527

Description (What does it do?)

Addresses accessibility issues in the Video Shorts modal and section carousel. The feature was previously unusable for keyboard-only and screenreader users due to unconditional preventDefault breaking all non-handled keys (tab, browser shortcuts), and the modal having no dialog semantics.

VideoShortsModal

  • Add FocusTrap, role="dialog", aria-modal so screenreaders announce it correctly
  • Fix preventDefault
  • Auto-focus Mute button on open (video starts muted)
  • Add aria-label/aria-pressed to Close, Mute, and a new Play/Pause button
  • Play/Pause button: invisible by default, visible on hover over the video and on keyboard focus
  • Add slide ARIA attributes (role, aria-roledescription, aria-label, tabIndex) for screenreader navigation
  • Fix Enter double-firing on Play/Pause by stopping propagation before it reaches the slide handler

VideoShortsSection

  • Make each slide the interactive target with role="button", aria-label, and keyboard handler

CarouselV2Vertical

  • Fix preventDefault

Screen Recording (if appropriate):

  • Without SR
video-shorts-carousel.mov
  • Using NVDA
nvda-video-shorts-carousel.mp4

How can this be tested?

  • Open the modal. Focus should automatically land on the Mute button
  • Tab through modal. The cycle is: Mute → Up → Down → Slide → Play/Pause → Close → (wraps back to Mute)

Note: When on the first video, the Up button is disabled and skipped — the tab cycle becomes Mute → Down → Slide → Play/Pause → Close → (wraps). Similarly, on the last video, Down is skipped. This is expected behavior.

  • Press Escape. The modal should close
  • When focused on the Play/Pause button, both Enter and Space toggle playback
  • When focused on the slide itself (before tabbing to the button), Enter toggles playback
  • Hover over video. Play/Pause button appears; move mouse off video. It should disappear immediately
  • Arrow keys navigate between videos; other keys (tab, browser shortcuts) are not blocked
  • Screenreader announces the modal as a dialog and each slide with its title and position
  • Tab through section carousel. Each slide should be focusable; Enter and Space open the modal
  • Arrow keys navigate between videos for keyboard-only users. Screen reader users use the Up/Down buttons (Tab to reach them, Enter/Space to activate); other keys (browser shortcuts etc.) are not blocked
  • With a screenreader active, use the Up/Down arrow buttons to navigate between videos; the new video title and position should be announced (e.g. "2 of 12: Video Title")

Additional Context

Known limitation (out of scope): The Featured Courses carousel has a focus trap issue. Once focus enters a card, Tab moves to the next focusable element within the card (e.g. bookmark button) rather than exiting the carousel. This needs a roving tabindex implementation similar to what was applied to the Video Shorts section. Tracked in https://github.com/mitodl/hq/issues/11575

Screenshot 2026-06-01 at 4 18 21 PM

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 29, 2026

OpenAPI Changes

No changes detected

View full changelog

Unexpected changes? Ensure your branch is up-to-date with main (consider rebasing).

@daniellefrappier18 daniellefrappier18 marked this pull request as ready for review June 1, 2026 15:30
Copilot AI review requested due to automatic review settings June 1, 2026 15:30
@daniellefrappier18 daniellefrappier18 added the Needs Review An open Pull Request that is ready for review label Jun 1, 2026

This comment was marked as outdated.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 6 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

frontends/main/src/app-pages/HomePage/VideoShortsModal.tsx:301

  • CarouselSlide is focusable and triggers playback on Enter, but it’s marked role="group" (non-interactive). Screen readers may not convey that it’s actionable. Either remove the Enter key handler (and keep it purely for slide navigation/announcement) or change the role/semantics to reflect interactivity (e.g., role="button" or move the keyboard activation onto an actual <button> element).
        player.muted(muted)
        // On iOS, only autoplay if muted or if user has interacted
        if (!isIOS() || muted || hasUserInteracted) {
          player.play()?.catch(() => {
            setPlaying(false)
          })
          setPlaying(true)
        }
      }
    }
  }

  const onClickMute = () => {
    if (selectedIndex !== null && playersRef.current[selectedIndex]) {

Comment thread frontends/main/src/app-pages/HomePage/VideoShortsSection.tsx
Comment thread frontends/main/src/app-pages/HomePage/VideoShortsSection.tsx
Comment thread frontends/main/src/app-pages/HomePage/VideoShortsModal.tsx
Comment thread frontends/main/src/app-pages/HomePage/VideoShortsModal.tsx
daniellefrappier18 and others added 4 commits June 1, 2026 14:51
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
  reliably focus it after FocusTrap initializes, rather than racing with it
- Add onSettle callback to CarouselV2 (fires after scroll animation with
  visible slide indices) to support live regions and roving tabindex
- Announce the first visible slide title via aria-live region in
  VideoShortsSection when the carousel arrow buttons are used
- Apply roving tabindex on section carousel slides so Tab enters at the
  first visible slide rather than always at slide 0
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 7 changed files in this pull request and generated 3 comments.

Comment thread frontends/ol-components/src/components/CarouselV2/CarouselV2.tsx
Comment thread frontends/main/src/app-pages/HomePage/VideoShortsModal.test.tsx Outdated
Comment thread frontends/main/src/app-pages/HomePage/VideoShortsModal.test.tsx
daniellefrappier18 and others added 3 commits June 1, 2026 16:13
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@shanbady shanbady self-assigned this Jun 2, 2026
Copy link
Copy Markdown
Contributor

@shanbady shanbady 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. worked as noted 👍

@shanbady shanbady removed the Needs Review An open Pull Request that is ready for review label Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants