4.9 KiB
4.9 KiB
1. Card component changes
- 1.1 Add a
modeprop (or equivalent) toStandardCard.astroto support rendering as a<button>(modal trigger) instead of an<a>link. Whenmode="modal", the root element MUST be a<button>(orrole="button" tabindex="0") with the same visual styling as the current card. - 1.2 Add
data-*attributes to modal-trigger cards inContentCard.astro— encodeid,source,url,title,summary,publishedAt,thumbnailUrl, andmetrics.viewsso the modal script can read them on click. - 1.3 Update
ContentCard.astroto usemode="modal"foryoutubeandpodcastsources, and keepmode="link"(current behavior) for other sources. - 1.4 Change Umami event from
outbound_clicktomedia_previewon modal-trigger cards. Updatedata-umami-event-*attributes to includetarget_id,placement,title,type, andsource(dropdomain,target_urlsince it's no longer outbound).
2. Media modal component
- 2.1 Create
MediaModal.astrocomponent containing a native<dialog>element with the modal layout: header (title + close button), embed area, description, date/views row, CTA row. - 2.2 Add CSS for the modal in
global.css: backdrop styling, modal container, responsive layout (centered on desktop, near-full-width on mobile), embed aspect-ratio (16:9 for video), loading placeholder. - 2.3 Write the modal open/populate script: listen for clicks on modal-trigger cards, read
data-*attributes, populate the modal fields (title, description, date, views), construct the embed URL, set the iframesrc, and calldialog.showModal(). - 2.4 Implement embed URL construction: extract YouTube video ID from
item.url→https://www.youtube.com/embed/{id}?rel=0&modestbranding=1. For podcast, detect Spotify URLs →https://open.spotify.com/embed/episode/{id}?theme=0. For non-Spotify podcast URLs, hide the embed area and show a "Listen on [platform]" link. - 2.5 Implement playback stop on close: on dialog
closeevent, set iframesrctoabout:blank(or remove the iframe) to guarantee playback stops. - 2.6 Implement all three close methods: close button click,
Escapekey (native<dialog>handles this), and backdrop click (detect clicks on<dialog>element outside the inner content container).
3. Modal accessibility
- 3.1 Set
aria-modal="true"andaria-labelledby(referencing the title element) on the<dialog>. - 3.2 Add
aria-label="Close"to the close button. - 3.3 Implement focus return: store a reference to the clicked card before opening, restore focus to it on close.
- 3.4 Verify focus trapping works with the native
<dialog>(Tab cycles through modal focusables only). - 3.5 Verify
prefers-reduced-motionsuppresses modal open/close animations (covered by the existing global CSS rule).
4. Modal CTAs
- 4.1 Render "Follow on YouTube" / "Follow on Spotify" CTA inside the modal, linking to the channel/podcast profile URL (from
LINKS.youtubeChannel/LINKS.podcast). Apply UTM parameters. - 4.2 Render "View on YouTube" / "View on Spotify" CTA inside the modal, linking to the specific content item URL. Apply UTM parameters.
- 4.3 Add
data-umami-event="cta_click"withtarget_id=modal.cta.{action}.{platform},placement=media_modal,platform, andtarget_urlon each modal CTA.
5. Umami analytics updates
- 5.1 Emit
media_preview_closeevent on modal close withtarget_id(from the card that opened it) andclose_method(button,escape, orbackdrop). Useumami.track()JS API sinceclose_methodis determined at runtime. - 5.2 Verify
media_previewevent fires correctly from carddata-umami-eventattributes on listing surfaces (homepage newest, homepage high-performing, homepage podcast, videos list, podcast list). - 5.3 Update existing Umami attribute tests (
site/tests/umami-attributes.test.ts) to expectmedia_previewinstead ofoutbound_clickfor video/podcast cards. - 5.4 Add new test cases for modal CTA Umami attributes (
cta_click,target_id,placement=media_modal).
6. Page integration
- 6.1 Add the
MediaModalcomponent toindex.astro(homepage). - 6.2 Add the
MediaModalcomponent tovideos.astro. - 6.3 Add the
MediaModalcomponent topodcast.astro.
7. Verification
- 7.1 Run
npm run buildinsite/and verify no build errors. - 7.2 Run
npm testinsite/and verify all tests pass (including updated Umami attribute tests). - 7.3 Manual smoke test: click a video card → modal opens with YouTube embed → close → playback stops. Repeat with Escape and backdrop click.
- 7.4 Manual smoke test: click a podcast card → modal opens with Spotify embed (or fallback link) → close → playback stops.
- 7.5 Verify modal accessibility: keyboard-only navigation, focus trap, focus return, screen reader announces dialog.
- 7.6 Verify responsive behavior: modal layout on desktop and mobile viewports.