From 25e50a73f712771ebd00179abe2acdebe80ab0b4 Mon Sep 17 00:00:00 2001 From: joshuasosa <4400438+joshuasosa@users.noreply.github.com> Date: Thu, 16 Apr 2026 11:07:58 -0700 Subject: [PATCH 1/2] Update text on media video play/pause buttons. --- .../js/az_paragraphs_az_text_media_vimeo.js | 62 ++++++++----------- .../js/az_paragraphs_az_text_media_youtube.js | 36 ++++++----- ...--az-remote-video--az-background.html.twig | 3 +- .../css/az_paragraphs_az_text_media.css | 7 --- 4 files changed, 49 insertions(+), 59 deletions(-) diff --git a/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_vimeo.js b/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_vimeo.js index 7ae28021bc..c653f8b152 100644 --- a/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_vimeo.js +++ b/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_vimeo.js @@ -67,26 +67,6 @@ } } - // Helper function for play button click - function handlePlayButtonClick(element, parentParagraph) { - return (event) => { - event.preventDefault(); - element.player.play().catch((error) => vimeoError(error)); - parentParagraph.classList.add('az-video-playing'); - parentParagraph.classList.remove('az-video-paused'); - }; - } - - // Helper function for pause button click - function handlePauseButtonClick(element, parentParagraph) { - return (event) => { - event.preventDefault(); - element.player.pause().catch((error) => vimeoError(error)); - parentParagraph.classList.add('az-video-paused'); - parentParagraph.classList.remove('az-video-playing'); - }; - } - // Helper function to initialize a single Vimeo element function initVimeoElement(element, defaultOptions) { const parentParagraph = element.parentNode; @@ -111,23 +91,33 @@ parentParagraph.classList.add('az-video-playing'); }); - // Play Button - const playButtons = element.getElementsByClassName('az-video-play'); - if (playButtons[0]) { - playButtons[0].addEventListener( - 'click', - handlePlayButtonClick(element, parentParagraph), - ); - } + // Set the iframe tabindex to -1 to prevent focus from reaching iframe. + element.player.ready().then(() => { + const iframe = element.player.element; + if (iframe) { + iframe.setAttribute('tabindex', '-1'); + } + }); - // Pause Button - const pauseButtons = element.getElementsByClassName('az-video-pause'); - if (pauseButtons[0]) { - pauseButtons[0].addEventListener( - 'click', - handlePauseButtonClick(element, parentParagraph), - ); - } + // Play/Pause button toggle. + const playPauseButton = + element.getElementsByClassName('az-video-playpause')[0]; + playPauseButton.addEventListener('click', (event) => { + event.preventDefault(); + if (event.currentTarget.textContent === 'Play Video') { + element.player.play().catch((error) => vimeoError(error)); + parentParagraph.classList.remove('az-video-paused'); + parentParagraph.classList.add('az-video-playing'); + event.currentTarget.textContent = 'Pause Video'; + event.currentTarget.setAttribute('title', 'Pause the video'); + } else { + element.player.pause().catch((error) => vimeoError(error)); + parentParagraph.classList.remove('az-video-playing'); + parentParagraph.classList.add('az-video-paused'); + event.currentTarget.textContent = 'Play Video'; + event.currentTarget.setAttribute('title', 'Play the video'); + } + }); } // Helper function to handle API loaded callback diff --git a/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_youtube.js b/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_youtube.js index bacdd26ca1..035e836106 100644 --- a/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_youtube.js +++ b/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_youtube.js @@ -54,21 +54,25 @@ onStateChange: window.onPlayerStateChange, }, }); - const playButton = - element.getElementsByClassName('az-video-play')[0]; - playButton.addEventListener('click', (event) => { - event.preventDefault(); - element.player.playVideo(); - parentParagraph.classList.remove('az-video-paused'); - parentParagraph.classList.add('az-video-playing'); - }); - const pauseButton = - element.getElementsByClassName('az-video-pause')[0]; - pauseButton.addEventListener('click', (event) => { + + // Play/Pause button toggle. + const playPauseButton = + element.getElementsByClassName('az-video-playpause')[0]; + playPauseButton.addEventListener('click', (event) => { event.preventDefault(); - element.player.pauseVideo(); - parentParagraph.classList.remove('az-video-playing'); - parentParagraph.classList.add('az-video-paused'); + if (event.currentTarget.textContent === 'Play Video') { + element.player.playVideo(); + parentParagraph.classList.remove('az-video-paused'); + parentParagraph.classList.add('az-video-playing'); + event.currentTarget.textContent = 'Pause Video'; + event.currentTarget.setAttribute('title', 'Pause the video'); + } else { + element.player.pauseVideo(); + parentParagraph.classList.remove('az-video-playing'); + parentParagraph.classList.add('az-video-paused'); + event.currentTarget.textContent = 'Play Video'; + event.currentTarget.setAttribute('title', 'Play the video'); + } }); }); }; @@ -119,6 +123,10 @@ }; window.onPlayerReady = (event) => { + // Set the iframe tabindex to -1 to prevent focus from reaching iframe. + const iframe = event.target.getIframe(); + iframe.setAttribute('tabindex', '-1'); + const id = event.target.options.videoId; if (!bgVideoSettings[id].autoplay) { return; diff --git a/modules/custom/az_paragraphs/az_paragraphs_text_media/templates/media--az-remote-video--az-background.html.twig b/modules/custom/az_paragraphs/az_paragraphs_text_media/templates/media--az-remote-video--az-background.html.twig index 9588142c1a..dfdc5b0e97 100644 --- a/modules/custom/az_paragraphs/az_paragraphs_text_media/templates/media--az-remote-video--az-background.html.twig +++ b/modules/custom/az_paragraphs/az_paragraphs_text_media/templates/media--az-remote-video--az-background.html.twig @@ -13,5 +13,4 @@
-
Pause Video
-
Play Video
+ diff --git a/modules/custom/az_paragraphs/css/az_paragraphs_az_text_media.css b/modules/custom/az_paragraphs/css/az_paragraphs_az_text_media.css index 17698d1155..646157d1be 100644 --- a/modules/custom/az_paragraphs/css/az_paragraphs_az_text_media.css +++ b/modules/custom/az_paragraphs/css/az_paragraphs_az_text_media.css @@ -49,7 +49,6 @@ right: 0; font-size: 0.9em; color: #0c234b; - z-index: -102; cursor: pointer; padding: 0.3em 0.6em; border: none; @@ -60,12 +59,6 @@ color: #ab0520; border-color: #ab0520; } -.az-video-playing .az-video-pause { - z-index: 2; -} -.az-video-paused .az-video-play { - z-index: 2; -} .az-video-container iframe { position: absolute; } From 691c1e8d83a6e6e30abfd499f3695777cac06374 Mon Sep 17 00:00:00 2001 From: Jeff Bishop Date: Tue, 5 May 2026 12:01:24 -0700 Subject: [PATCH 2/2] =?UTF-8?q?a11y:=20video=20play/pause=20button=20?= =?UTF-8?q?=E2=80=94=20aria-pressed,=20event-driven=20state,=20fix=20aria-?= =?UTF-8?q?hidden=20(closes=20#5515)=20(#5516)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * a11y: video button aria-pressed state, event-driven sync, fix aria-hidden Closes #5515 - Remove aria-hidden="" from button (was hiding it from AT) - Remove redundant title attribute (visible text content is sufficient) - Set aria-pressed="true" as initial default (conservative: video not confirmed playing until player fires its play event) - Change button initial label to "Play Video" (matches aria-pressed state) Vimeo: - Replace textContent comparison in click handler with aria-pressed check - Replace bufferend class management with dedicated play/pause events - play event: sets "Pause Video" + aria-pressed="false" + classes - pause event: sets "Play Video" + aria-pressed="true" + classes - Click handler: only calls play()/pause(); events drive all state YouTube: - Replace textContent comparison in click handler with aria-pressed check - onPlayerStateChange state 1: syncs button to "Pause Video" + aria-pressed="false" - onPlayerStateChange state 2: syncs button to "Play Video" + aria-pressed="true" - Both cases also manage az-video-playing/az-video-paused classes * fix(lint): fix prettier line-break in vimeo.js (closes CI eslint failure) Revert the split template literal at L48 back to a single line as required by prettier. The line is within the 80-char print-width limit so no wrap is needed. * fix(lint): fix prettier and no-unused-vars errors in youtube.js - Revert split `if (window.screen && ...)` back to one line (L5) - Revert split `const firstScriptTag` back to one line (L20) - Remove unused `parentParagraph` variable (L31): class state updates were moved to onPlayerStateChange; the click handler no longer needs to reference the parent container directly - Wrap long `getAttribute('aria-pressed')` condition per prettier print-width rules (L65) * fix(a11y): address 7 Copilot review comments on video play/pause button - template: fix initial aria-pressed from 'true' to 'false' (video not yet playing on load) - youtube.js: invert click handler so aria-pressed='true' (playing) calls pauseVideo() - youtube.js: add classList.remove('az-video-paused') when entering playing state - youtube.js: set aria-pressed='true' in playing state, 'false' in paused state - vimeo.js: set aria-pressed='true' in play event, 'false' in pause event - vimeo.js: invert click handler so aria-pressed='true' (playing) calls pause() All changes align aria-pressed semantics: true = video is playing, false = video is not playing, matching the ARIA toggle button pattern. Addresses Copilot review on #5516. --- .../js/az_paragraphs_az_text_media_vimeo.js | 40 +++++++++++-------- .../js/az_paragraphs_az_text_media_youtube.js | 39 +++++++++++------- ...--az-remote-video--az-background.html.twig | 2 +- 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_vimeo.js b/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_vimeo.js index c653f8b152..e36237108b 100644 --- a/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_vimeo.js +++ b/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_vimeo.js @@ -85,10 +85,29 @@ muted: defaultOptions.muted, }); - // Event listener for starting play. + // Play/Pause button reference used by event handlers below. + const playPauseButton = + element.getElementsByClassName('az-video-playpause')[0]; + + // Update dimensions when buffering completes. element.player.on('bufferend', () => { setDimensions(element); + }); + + // Sync button and class state when video plays. + element.player.on('play', () => { + parentParagraph.classList.remove('az-video-paused'); parentParagraph.classList.add('az-video-playing'); + playPauseButton.textContent = 'Pause Video'; + playPauseButton.setAttribute('aria-pressed', 'true'); + }); + + // Sync button and class state when video pauses. + element.player.on('pause', () => { + parentParagraph.classList.remove('az-video-playing'); + parentParagraph.classList.add('az-video-paused'); + playPauseButton.textContent = 'Play Video'; + playPauseButton.setAttribute('aria-pressed', 'false'); }); // Set the iframe tabindex to -1 to prevent focus from reaching iframe. @@ -99,23 +118,13 @@ } }); - // Play/Pause button toggle. - const playPauseButton = - element.getElementsByClassName('az-video-playpause')[0]; + // Play/Pause button: delegate state changes to player events. playPauseButton.addEventListener('click', (event) => { event.preventDefault(); - if (event.currentTarget.textContent === 'Play Video') { - element.player.play().catch((error) => vimeoError(error)); - parentParagraph.classList.remove('az-video-paused'); - parentParagraph.classList.add('az-video-playing'); - event.currentTarget.textContent = 'Pause Video'; - event.currentTarget.setAttribute('title', 'Pause the video'); - } else { + if (event.currentTarget.getAttribute('aria-pressed') === 'true') { element.player.pause().catch((error) => vimeoError(error)); - parentParagraph.classList.remove('az-video-playing'); - parentParagraph.classList.add('az-video-paused'); - event.currentTarget.textContent = 'Play Video'; - event.currentTarget.setAttribute('title', 'Play the video'); + } else { + element.player.play().catch((error) => vimeoError(error)); } }); } @@ -165,7 +174,6 @@ }); } } - once('vimeoTextOnMedia-init', 'body').forEach(initVimeoBackgrounds); }, }; diff --git a/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_youtube.js b/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_youtube.js index 035e836106..094aa0b3a0 100644 --- a/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_youtube.js +++ b/modules/custom/az_paragraphs/az_paragraphs_text_media/js/az_paragraphs_az_text_media_youtube.js @@ -26,9 +26,6 @@ ); window.onYouTubeIframeAPIReady = () => { Array.from(bgVideoParagraphs).forEach((element) => { - const parentParagraph = document.getElementById( - element.dataset.parentid, - ); const youtubeId = element.dataset.youtubeid; bgVideoSettings[youtubeId] = { autoplay: element.dataset.autoplay === 'true', @@ -55,23 +52,17 @@ }, }); - // Play/Pause button toggle. + // Play/Pause button: delegate state changes to player events. const playPauseButton = element.getElementsByClassName('az-video-playpause')[0]; playPauseButton.addEventListener('click', (event) => { event.preventDefault(); - if (event.currentTarget.textContent === 'Play Video') { - element.player.playVideo(); - parentParagraph.classList.remove('az-video-paused'); - parentParagraph.classList.add('az-video-playing'); - event.currentTarget.textContent = 'Pause Video'; - event.currentTarget.setAttribute('title', 'Pause the video'); - } else { + if ( + event.currentTarget.getAttribute('aria-pressed') === 'true' + ) { element.player.pauseVideo(); - parentParagraph.classList.remove('az-video-playing'); - parentParagraph.classList.add('az-video-paused'); - event.currentTarget.textContent = 'Play Video'; - event.currentTarget.setAttribute('title', 'Play the video'); + } else { + element.player.playVideo(); } }); }); @@ -134,6 +125,7 @@ if (defaultSettings.mute) { event.target.mute(); } + event.target.seekTo(bgVideoSettings[id].start); event.target.playVideo(); // Create and dispatch a new event when video starts playing. @@ -155,7 +147,24 @@ if (event.data === 1) { resize(); parentContainer.classList.add('az-video-playing'); + parentContainer.classList.remove('az-video-paused'); parentContainer.classList.remove('az-video-loading'); + // Sync button state: video is confirmed playing. + const btn = parentContainer.querySelector('.az-video-playpause'); + if (btn) { + btn.textContent = 'Pause Video'; + btn.setAttribute('aria-pressed', 'true'); + } + } + if (event.data === 2) { + // Video paused: sync button state. + parentContainer.classList.remove('az-video-playing'); + parentContainer.classList.add('az-video-paused'); + const btn = parentContainer.querySelector('.az-video-playpause'); + if (btn) { + btn.textContent = 'Play Video'; + btn.setAttribute('aria-pressed', 'false'); + } } }; diff --git a/modules/custom/az_paragraphs/az_paragraphs_text_media/templates/media--az-remote-video--az-background.html.twig b/modules/custom/az_paragraphs/az_paragraphs_text_media/templates/media--az-remote-video--az-background.html.twig index dfdc5b0e97..14372f8a28 100644 --- a/modules/custom/az_paragraphs/az_paragraphs_text_media/templates/media--az-remote-video--az-background.html.twig +++ b/modules/custom/az_paragraphs/az_paragraphs_text_media/templates/media--az-remote-video--az-background.html.twig @@ -13,4 +13,4 @@
- +