Files
2026-02-10 22:37:29 -05:00

6.2 KiB

Purpose

Define the media modal dialog behavior for in-page video and podcast previews.

Requirements

Requirement: Media modal dialog

The site MUST provide a modal dialog that displays embedded media (YouTube video or podcast episode) when a user clicks a video or podcast content card on a listing surface (homepage, /videos, /podcast).

The modal MUST render the following elements in order:

  • A header row with the content title on the left and a close button on the right
  • An embedded media player (YouTube iframe for videos; Spotify embed for podcast episodes when the URL is a Spotify URL; otherwise an in-modal audio player when an audioUrl is available)
  • The full description/summary text (not truncated)
  • The publish date and view count (when available)
  • A "Subscribe on YouTube" / "Follow on Spotify" CTA and a "View on YouTube" / "Listen on Spotify" CTA

All modal CTAs that represent navigation MUST be implemented as crawlable anchors:

  • Each CTA MUST be an <a> element with a non-empty href attribute.
  • The UI MUST NOT render placeholder <a> elements without href in the initial HTML.
  • If CTA destinations are not known until a user selects an item, the CTA UI MUST be rendered as non-anchor elements until the destinations are known.

Scenario: User clicks a YouTube video card

  • WHEN a user clicks a video content card on any listing surface
  • THEN a modal dialog opens displaying a YouTube iframe embed, the video title, full description, date, view count (if available), and CTAs for "Subscribe on YouTube" and "View on YouTube"

Scenario: User clicks a podcast episode card with a Spotify URL

  • WHEN a user clicks a podcast content card whose URL is a Spotify URL
  • THEN a modal dialog opens displaying a Spotify episode embed, the episode title, full description, date, and CTAs for "Follow on Spotify" and "Listen on Spotify"

Scenario: User clicks a podcast episode card with a non-Spotify URL

  • WHEN a user clicks a podcast content card whose URL is not a Spotify URL
  • THEN the modal dialog opens displaying the episode metadata (title, description, date) and either:
    • an in-modal audio player when an audioUrl is available
    • otherwise, a "Listen on Spotify" outbound link

Scenario: Modal renders with missing optional fields

  • WHEN a content item has no view count or no summary
  • THEN the modal MUST still render cleanly with those fields omitted

Scenario: Modal CTAs are crawlable anchors

  • WHEN the modal is present in the DOM (before any user interaction)
  • THEN the document contains no <a> elements in the modal that are missing href

If an embed fallback is presented as a link to an external page, it MUST be an anchor with a valid href. If no destination is available, the fallback MUST be hidden or rendered as non-link text.

Scenario: Embed fallback does not render a non-crawlable anchor

  • WHEN the modal is rendered before any item selection
  • THEN the embed fallback is not rendered as an anchor without href

Requirement: Playback stops on modal close

The modal MUST stop all media playback when it is dismissed, regardless of the dismissal method.

The modal MUST support three dismissal methods:

  • Close button click
  • Pressing the Escape key
  • Clicking the backdrop outside the modal content

After dismissal, no audio or video from the embedded player MUST continue playing.

Scenario: User closes modal via close button

  • WHEN the modal is open with a playing YouTube video and the user clicks the close button
  • THEN the modal closes and the video playback stops immediately

Scenario: User presses Escape while modal is open

  • WHEN the modal is open with a playing Spotify episode and the user presses the Escape key
  • THEN the modal closes and the audio playback stops immediately

Scenario: User clicks the backdrop

  • WHEN the modal is open and the user clicks outside the modal content area (the backdrop)
  • THEN the modal closes and any active media playback stops immediately

Requirement: Modal accessibility

The modal MUST conform to WCAG 2.2 AA dialog patterns:

  • The modal MUST use the native <dialog> element opened via showModal()
  • The modal MUST trap keyboard focus within the dialog while open
  • The modal MUST set aria-modal="true" and have an accessible label (via aria-labelledby referencing the title element)
  • Closing the modal MUST return focus to the element that triggered it (the card that was clicked)
  • The close button MUST have an accessible label (e.g., aria-label="Close")

Scenario: Focus is trapped within the modal

  • WHEN the modal is open and the user presses Tab
  • THEN focus cycles through the focusable elements within the modal and does not move to elements behind the modal

Scenario: Focus returns to trigger on close

  • WHEN the user closes the modal
  • THEN focus returns to the card element that originally opened the modal

Requirement: Responsive modal layout

The modal MUST be responsive across viewports:

  • On desktop viewports, the modal MUST be centered with a max-width that leaves visible backdrop on both sides
  • On mobile viewports (at or below the site's mobile breakpoint), the modal MUST expand to near-full viewport width with reduced padding
  • Embedded media MUST scale proportionally (16:9 aspect ratio for YouTube video, fixed height for Spotify embed)

Scenario: Modal on desktop viewport

  • WHEN the modal is opened on a desktop viewport
  • THEN the modal is centered horizontally with backdrop visible and the video embed maintains a 16:9 aspect ratio

Scenario: Modal on mobile viewport

  • WHEN the modal is opened on a mobile viewport
  • THEN the modal expands to near-full viewport width and the video embed scales to fit

Requirement: Embed loading state

The modal MUST display a loading placeholder while the embedded media iframe is loading.

Scenario: Iframe loading

  • WHEN the modal opens and the iframe has not yet loaded
  • THEN a placeholder (matching the site's card-placeholder style) is visible in the embed area until the iframe finishes loading