From 415b5436e2dcce1f5b7559940e1d7e23301d1fba Mon Sep 17 00:00:00 2001 From: omsherikar Date: Fri, 24 Oct 2025 03:47:27 +0530 Subject: [PATCH 1/4] feat(videos): implement embedded video player with ultra-large modal - Add beautiful video modal with YouTube/Vimeo embed support - Implement ultra-large responsive modal (95vw width, 70-85vh height) - Add smooth animations and loading states with gradient backgrounds - Include full accessibility support with ARIA labels and focus management - Use semantic HTML5 elements (dialog, header, section) for better structure - Follow Tailwind CSS best practices with @layer components - Add keyboard navigation and screen reader support - Implement mobile-first responsive design with dark mode support - Replace YouTube redirects with embedded video player - Maintain CONTRIBUTING.md compliance throughout Closes #620 --- web/templates/videos/list.html | 284 +++++++++++++++++++++++++++++++-- 1 file changed, 273 insertions(+), 11 deletions(-) diff --git a/web/templates/videos/list.html b/web/templates/videos/list.html index 3be0307fa..71134931d 100644 --- a/web/templates/videos/list.html +++ b/web/templates/videos/list.html @@ -22,6 +22,125 @@ transform: translateY(0); } } + + /* Video Modal - Following Tailwind Best Practices */ + @layer components { + .video-modal { + @apply hidden fixed inset-0 z-50 bg-black bg-opacity-90 backdrop-blur-sm opacity-0 transition-all duration-300 ease-out; + } + + .video-modal.show { + @apply flex items-center justify-center opacity-100; + } + + .video-modal-content { + @apply relative bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900 dark:from-gray-800 dark:via-gray-700 dark:to-gray-800 rounded-3xl overflow-hidden max-w-[95vw] w-full mx-2 shadow-2xl border border-white border-opacity-20 transform scale-95 transition-all duration-300 ease-out; + box-shadow: + 0 32px 64px -12px rgba(0, 0, 0, 0.6), + 0 0 0 1px rgba(255, 255, 255, 0.1), + inset 0 1px 0 rgba(255, 255, 255, 0.1); + } + + .video-modal.show .video-modal-content { + @apply scale-100; + } + + .video-modal-header { + @apply flex justify-between items-center px-6 py-5 bg-gradient-to-r from-gray-800 via-gray-700 to-gray-800 dark:from-gray-700 dark:via-gray-600 dark:to-gray-700 border-b border-gray-600 relative; + background: linear-gradient(135deg, #1f2937, #374151, #1f2937); + } + + .video-modal-header::after { + content: ''; + @apply absolute bottom-0 left-0 right-0 h-px bg-gradient-to-r from-transparent via-white to-transparent opacity-30; + } + + .video-modal-title { + @apply text-white text-xl font-bold m-0 tracking-tight; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); + } + + .video-modal-close { + @apply bg-white bg-opacity-10 border border-white border-opacity-20 text-white text-xl cursor-pointer p-2 rounded-lg transition-all duration-200 ease-out w-10 h-10 flex items-center justify-center hover:bg-opacity-20 hover:border-opacity-30 hover:scale-105 active:scale-95 focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 focus:outline-none; + } + + .video-modal-body { + @apply relative; + } + + .video-modal-iframe { + @apply w-full h-[70vh] sm:h-[75vh] lg:h-[80vh] xl:h-[85vh] border-0 rounded-b-3xl; + background: linear-gradient(45deg, #1a1a1a, #2a2a2a); + } + + .video-card { + @apply transition-all duration-300 ease-out relative overflow-hidden; + } + + .video-card:hover { + @apply -translate-y-1 shadow-xl; + } + + .video-card .video-link i.fa-play { + @apply transition-all duration-300 ease-out; + } + + .video-card:hover .video-link i.fa-play { + @apply scale-110; + } + + .sr-only { + @apply absolute w-px h-px p-0 -m-px overflow-hidden whitespace-nowrap border-0; + clip: rect(0, 0, 0, 0); + } + } + + /* Enhanced Responsive Design - Mobile First */ + @media (max-width: 768px) { + .video-modal-content { + @apply mx-1 rounded-2xl; + max-width: 98vw; + } + + .video-modal-iframe { + @apply h-[65vh]; + } + + .video-modal-header { + @apply px-4 py-4; + } + + .video-modal-title { + @apply text-lg; + } + + .video-modal-close { + @apply w-9 h-9 p-1.5; + } + } + + @media (max-width: 480px) { + .video-modal-content { + @apply mx-0.5 rounded-xl; + max-width: 99vw; + } + + .video-modal-iframe { + @apply h-[60vh]; + } + + .video-modal-header { + @apply px-3 py-3; + } + + .video-modal-title { + @apply text-base; + } + + .video-modal-close { + @apply w-8 h-8 p-1; + } + } {% endblock %} {% block content %} @@ -93,24 +212,24 @@

{% if video.thumbnail_url %} -
+ {% elif video.thumbnail_url %} -
+ @@ -123,9 +242,9 @@

- {{ video.title }} + {{ video.title }}

{% if video.uploader %} @@ -304,4 +423,147 @@

Request an Educational Video

+ + + +
+
+

+ +
+
+ +
+
+
+ + {% endblock %} From ff36eaa86c19626296bc7c12e6fbe3a4551ec2b1 Mon Sep 17 00:00:00 2001 From: omsherikar Date: Fri, 24 Oct 2025 03:55:12 +0530 Subject: [PATCH 2/4] fix(videos): remove duplicate video thumbnail markup - Remove redundant {% elif video.thumbnail_url %} block - Fix copy-paste error that created identical conditional blocks - Clean up template structure for better maintainability The second condition was unreachable since the first already handled the video.thumbnail_url case. --- web/templates/videos/list.html | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/web/templates/videos/list.html b/web/templates/videos/list.html index 71134931d..fea37f562 100644 --- a/web/templates/videos/list.html +++ b/web/templates/videos/list.html @@ -222,17 +222,6 @@

- {% elif video.thumbnail_url %} -
- {{ video.title }} thumbnail - - - -
{% elif 'vimeo.com' in video.video_url %} {% else %} From ba3daaf90ccc0935c4bdfdc37c5e212fc4e67bac Mon Sep 17 00:00:00 2001 From: omsherikar Date: Fri, 24 Oct 2025 03:59:12 +0530 Subject: [PATCH 3/4] fix(accessibility): improve focus restoration in video modal - Store reference to triggering element for reliable focus restoration - Pass event object to openVideoModal function to capture caller - Replace unreliable focus detection with explicit element reference - Add proper cleanup of triggering element reference - Ensure focus returns to the exact element that opened the modal - Improve screen reader experience and keyboard navigation Fixes focus restoration issues where focus could shift during modal interaction, making the close button the active element instead of the original triggering link. --- web/templates/videos/list.html | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/web/templates/videos/list.html b/web/templates/videos/list.html index fea37f562..7cfec38bb 100644 --- a/web/templates/videos/list.html +++ b/web/templates/videos/list.html @@ -217,7 +217,7 @@

alt="{{ video.title }} thumbnail" class="w-full h-full object-cover" /> @@ -232,7 +232,7 @@

{{ video.title }}

@@ -443,6 +443,9 @@

const videoModalIframe = document.getElementById('videoModalIframe'); const videoModalClose = document.getElementById('videoModalClose'); + // Store reference to the element that opened the modal for proper focus restoration + let triggeringElement = null; + // Function to extract YouTube video ID function getYouTubeVideoId(url) { const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/; @@ -458,7 +461,10 @@

} // Function to open video modal - function openVideoModal(videoUrl, videoTitle) { + function openVideoModal(videoUrl, videoTitle, event) { + // Capture the element that triggered the modal for proper focus restoration + triggeringElement = event?.target || document.activeElement; + let embedUrl = ''; const youtubeId = getYouTubeVideoId(videoUrl); const vimeoId = getVimeoVideoId(videoUrl); @@ -510,10 +516,13 @@

videoModalIframe.src = ''; document.body.style.overflow = ''; - // Return focus to the video link that opened the modal - if (document.activeElement && document.activeElement.classList.contains('video-link')) { - document.activeElement.focus(); + // Restore focus to the element that opened the modal + if (triggeringElement && document.contains(triggeringElement)) { + triggeringElement.focus(); } + + // Clear the reference + triggeringElement = null; }, 300); } From 079128eebd854d5eec51eec7744e82b7bfe5b842 Mon Sep 17 00:00:00 2001 From: omsherikar Date: Fri, 24 Oct 2025 04:01:51 +0530 Subject: [PATCH 4/4] fix(accessibility): improve tab trapping and keyboard navigation - Enhance tab trapping logic to handle edge cases properly - Add disabled element filtering to focusable elements selector - Handle case where no focusable elements exist (prevent tab escape) - Improve focus order by focusing title first for screen readers - Add tabindex to modal title for better keyboard accessibility - Ensure robust tab cycling between first and last elements - Fix potential issues with iframe focusability The improved implementation now properly handles: - Disabled form elements (excluded from tab order) - Empty focusable elements list (prevents tab escape) - Better focus order (title -> close button) - More robust element selection criteria --- web/templates/videos/list.html | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/web/templates/videos/list.html b/web/templates/videos/list.html index 7cfec38bb..9351fd8d6 100644 --- a/web/templates/videos/list.html +++ b/web/templates/videos/list.html @@ -417,7 +417,7 @@

Request an Educational Video

-

+