fix for SR
Some checks failed
ci / site (push) Has been cancelled
publish-image / publish (push) Has been cancelled

This commit is contained in:
2026-02-10 17:54:13 -05:00
parent 5d07e57256
commit 57ad560b01
18 changed files with 389 additions and 110 deletions

View File

@@ -17,6 +17,8 @@ When Umami is disabled or not configured, the site MUST still function and MUST
When Umami is enabled, the site MUST support custom event emission for:
- `cta_click`
- `outbound_click`
- `media_preview`
- `media_preview_close`
- a general click interaction event for all instrumented clickable items (per the site tracking taxonomy)
Each emitted event MUST include enough properties to segment reports by platform and placement when applicable.
@@ -27,6 +29,8 @@ The site MUST instrument tracked clickables using Umami's supported Track Events
- `data-umami-event="<event-name>"`
- optional event data using `data-umami-event-*`
For interactions that are triggered programmatically (e.g., modal close events where the close method must be recorded), the site MAY use Umami's JavaScript API (`umami.track()`) instead of data attributes when data attributes cannot express the required properties.
For content-related links (clickables representing a specific piece of content), the site MUST also provide the following Umami event data attributes:
- `data-umami-event-title`
- `data-umami-event-type`
@@ -47,6 +51,18 @@ For content-related links (clickables representing a specific piece of content),
- **WHEN** a user clicks an element with no tracking metadata
- **THEN** the system does not throw and navigation/interaction proceeds normally
#### Scenario: Media preview event emitted on card click
- **WHEN** a user clicks a video or podcast content card that opens a media modal
- **THEN** the system emits a `media_preview` event with `target_id`, `placement`, `title`, `type`, and `source`
#### Scenario: Media preview close event emitted
- **WHEN** a user closes the media modal
- **THEN** the system emits a `media_preview_close` event with the `target_id` of the content that was previewed and the `close_method` used
#### Scenario: Modal CTA click emitted
- **WHEN** a user clicks a CTA inside the media modal (e.g., "View on YouTube")
- **THEN** the system emits a `cta_click` event with `target_id`, `placement=media_modal`, `platform`, and `target_url`
### Requirement: Environment configuration
The site MUST support configuration of Umami parameters (at minimum: website ID and script URL) without requiring code changes.

View File

@@ -18,6 +18,12 @@ The standard card layout MUST be:
If a field is not available (for example, views for some sources), the card MUST still render cleanly with that field omitted.
For content cards with source `youtube` or `podcast`, the card MUST render as a clickable element that opens a media modal dialog instead of navigating to an external URL. The card MUST NOT render as an outbound `<a>` link for these sources.
For content cards with other sources (e.g., `blog`, `instagram`), the card MUST continue to render as a navigational link (the existing behavior).
The card element for modal-trigger cards MUST carry the content item's data (id, source, url, title, summary, publishedAt, thumbnailUrl, views) as `data-*` attributes so the modal script can access them.
#### Scenario: Card renders with all fields
- **WHEN** a content item has an image, title, summary, publish date, views, and source
- **THEN** the card renders those fields in the standard card layout order
@@ -37,3 +43,15 @@ If a field is not available (for example, views for some sources), the card MUST
#### Scenario: Card image load failure shows static placeholder
- **WHEN** a content item has an image URL but the image fails to load
- **THEN** the card media area displays a static placeholder (no broken image icon) and the card remains visually intact
#### Scenario: YouTube video card opens modal
- **WHEN** a user clicks a content card with source `youtube`
- **THEN** a media modal dialog opens with the video's embedded player and metadata instead of navigating to YouTube
#### Scenario: Podcast card opens modal
- **WHEN** a user clicks a content card with source `podcast`
- **THEN** a media modal dialog opens with the episode's embedded player (or metadata link) instead of navigating to the podcast platform
#### Scenario: Blog card still navigates
- **WHEN** a user clicks a content card with source `blog`
- **THEN** the card navigates to the blog post as an internal link (existing behavior, unaffected)

View File

@@ -38,3 +38,50 @@ In addition, CTA clicks MUST conform to the site click tracking taxonomy and MUS
#### Scenario: Two CTAs to the same destination
- **WHEN** two CTAs link to the same destination but appear in different placements
- **THEN** their emitted events contain different `target_id` values
### Requirement: Modal CTAs for YouTube and Spotify
The media modal MUST render two CTA actions:
- "Subscribe on YouTube" / "Follow on Spotify" — links to the channel/podcast profile page
- "View on YouTube" / "Listen on Spotify" — links to the specific content item URL
The CTA label and destination MUST be determined by the content source:
- For `youtube` source: "Subscribe on YouTube" links to the YouTube channel URL, "View on YouTube" links to the video URL
- For `podcast` source: "Follow on Spotify" links to the podcast profile URL, "Listen on Spotify" links to the episode URL
Each CTA MUST be rendered using the existing `CtaLink` component conventions (or equivalent markup) with UTM parameters appended.
#### Scenario: YouTube video modal shows YouTube CTAs
- **WHEN** the media modal is displaying a YouTube video
- **THEN** the modal renders "Subscribe on YouTube" (linking to the channel) and "View on YouTube" (linking to the video URL) as CTA actions
#### Scenario: Podcast episode modal shows Spotify CTAs
- **WHEN** the media modal is displaying a podcast episode
- **THEN** the modal renders "Follow on Spotify" (linking to the podcast profile) and "Listen on Spotify" (linking to the episode URL) as CTA actions
### Requirement: Modal CTA tracking
Each CTA rendered inside the media modal MUST emit a `cta_click` event conforming to the existing CTA tracking requirements.
The modal CTAs MUST use:
- `placement=media_modal`
- `target_id` following the `modal.cta.{action}.{platform}` namespace
- `platform` set to `youtube` or `spotify` (mapped from content source)
#### Scenario: Modal CTA emits cta_click event
- **WHEN** a user clicks the "Subscribe on YouTube" CTA inside the media modal
- **THEN** the system emits a `cta_click` event with `target_id=modal.cta.subscribe.youtube`, `placement=media_modal`, `platform=youtube`, and `target_url` set to the YouTube channel URL
#### Scenario: Modal CTA emits cta_click event (secondary)
- **WHEN** a user clicks the "View on YouTube" CTA inside the media modal
- **THEN** the system emits a `cta_click` event with `target_id=modal.cta.view.youtube`, `placement=media_modal`, `platform=youtube`, and `target_url` set to the video URL
#### Scenario: Modal CTA emits cta_click event (podcast)
- **WHEN** a user clicks the "Follow on Spotify" CTA inside the media modal
- **THEN** the system emits a `cta_click` event with `target_id=modal.cta.follow.spotify`, `placement=media_modal`, `platform=spotify`, and `target_url` set to the podcast profile URL
#### Scenario: Modal CTA emits cta_click event (podcast secondary)
- **WHEN** a user clicks the "Listen on Spotify" CTA inside the media modal
- **THEN** the system emits a `cta_click` event with `target_id=modal.cta.listen.spotify`, `placement=media_modal`, `platform=spotify`, and `target_url` set to the episode URL
#### Scenario: Modal CTA includes UTM parameters
- **WHEN** a modal CTA is rendered
- **THEN** the CTA link URL includes UTM parameters for attribution (utm_source, utm_medium, utm_campaign, utm_content)

View File

@@ -28,6 +28,70 @@ The taxonomy MUST define namespaces for repeated UI surfaces. For the blog surfa
- **WHEN** two links point to the same destination but appear in different placements
- **THEN** their `target_id` values are different so their clicks can be measured independently
### Requirement: Media preview event for in-page content views
The tracking taxonomy MUST define a `media_preview` event for content card clicks that open an in-page media modal instead of navigating outbound.
The `media_preview` event MUST include the following properties:
- `target_id` (stable unique identifier for the card, following the existing `card.{placement}.{source}.{id}` format)
- `placement` (the listing surface where the card appears, e.g., `home.newest`, `videos.list`, `podcast.list`)
- `title` (human-readable content title, truncated to max 160 characters)
- `type` (`video` or `podcast_episode`)
- `source` (`youtube` or `podcast`)
#### Scenario: Video card click emits media_preview
- **WHEN** a user clicks a YouTube video card on the videos listing page
- **THEN** the system emits a `media_preview` event with `target_id=card.videos.list.youtube.{id}`, `placement=videos.list`, `type=video`, and `source=youtube`
#### Scenario: Podcast card click emits media_preview on homepage
- **WHEN** a user clicks a podcast card in the homepage podcast section
- **THEN** the system emits a `media_preview` event with `target_id=card.home.podcast.podcast.{id}`, `placement=home.podcast`, `type=podcast_episode`, and `source=podcast`
### Requirement: Media preview close event
The tracking taxonomy MUST define a `media_preview_close` event emitted when the media modal is dismissed.
The `media_preview_close` event MUST include:
- `target_id` (the same identifier as the `media_preview` event that opened the modal)
- `close_method` (one of: `button`, `escape`, `backdrop`)
#### Scenario: User closes modal via Escape key
- **WHEN** the user presses Escape to close the media modal that was opened from a video card
- **THEN** the system emits a `media_preview_close` event with the original `target_id` and `close_method=escape`
#### Scenario: User closes modal via close button
- **WHEN** the user clicks the close button on the media modal
- **THEN** the system emits a `media_preview_close` event with the original `target_id` and `close_method=button`
#### Scenario: User closes modal via backdrop click
- **WHEN** the user clicks the backdrop outside the modal content
- **THEN** the system emits a `media_preview_close` event with the original `target_id` and `close_method=backdrop`
### Requirement: Modal CTA namespace
The tracking taxonomy MUST define a `media_modal` placement value for CTA interactions within the media modal.
Modal CTAs MUST use `target_id` values in the namespace `modal.cta.{action}.{platform}`.
The `action` value MUST be one of:
- `subscribe` (YouTube channel)
- `view` (YouTube video)
- `follow` (Spotify podcast profile)
- `listen` (Spotify episode)
#### Scenario: User clicks "View on YouTube" in modal
- **WHEN** the user clicks the "View on YouTube" CTA inside the media modal
- **THEN** the system emits a `cta_click` event with `target_id=modal.cta.view.youtube`, `placement=media_modal`, and `platform=youtube`
#### Scenario: User clicks "Subscribe on YouTube" in modal
- **WHEN** the user clicks the "Subscribe on YouTube" CTA inside the media modal
- **THEN** the system emits a `cta_click` event with `target_id=modal.cta.subscribe.youtube`, `placement=media_modal`, and `platform=youtube`
#### Scenario: User clicks "Follow on Spotify" in modal
- **WHEN** the user clicks the "Follow on Spotify" CTA inside the media modal
- **THEN** the system emits a `cta_click` event with `target_id=modal.cta.follow.spotify`, `placement=media_modal`, and `platform=spotify`
#### Scenario: User clicks "Listen on Spotify" in modal
- **WHEN** the user clicks the "Listen on Spotify" CTA inside the media modal
- **THEN** the system emits a `cta_click` event with `target_id=modal.cta.listen.spotify`, `placement=media_modal`, and `platform=spotify`
### Requirement: Minimum required properties
Every tracked click event MUST include, at minimum:
- `target_id`
@@ -46,6 +110,11 @@ The `type` value MUST be one of:
- `blog_post`
- `blog_page`
For non-link clickables that trigger in-page actions (e.g., modal openers), the event MUST also include:
- `title` (human-readable content title)
- `type` (content type identifier)
- `source` (content source identifier)
#### Scenario: Tracking a content card click
- **WHEN** a user clicks a content card link
- **THEN** the emitted event includes `target_id`, `placement`, and `target_url`
@@ -54,6 +123,10 @@ The `type` value MUST be one of:
- **WHEN** a user clicks a content-related link that represents a specific content item
- **THEN** the emitted event includes `target_id`, `placement`, `target_url`, `title`, and `type`
#### Scenario: Tracking a modal-trigger card includes title, type, and source
- **WHEN** a user clicks a content card that opens a media modal instead of navigating
- **THEN** the emitted event includes `target_id`, `placement`, `title`, `type`, and `source` (no `target_url` since the user stays on-page)
### Requirement: No PII in event properties
The taxonomy MUST prohibit including personally identifiable information (PII) in event names or event properties.

View File

@@ -0,0 +1,92 @@
## 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
#### 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
### 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