From ef07bd86e23c8d56ba59143379c87530057aaf78 Mon Sep 17 00:00:00 2001 From: Yiannis Christodoulou Date: Fri, 3 Oct 2025 13:00:30 +0300 Subject: [PATCH] Improve end screen overlay layout and touch support Refines EndScreenOverlay CSS for better spacing, grid alignment, and responsive behavior across various screen sizes and embed heights. Adds touch device detection in JS to show overlays and durations by default for improved usability on mobile devices. Limits related video items to 2 for small embed heights to enhance readability. --- .../components/overlays/EndScreenOverlay.css | 225 +++++++++++------- .../components/overlays/EndScreenOverlay.js | 28 ++- 2 files changed, 169 insertions(+), 84 deletions(-) diff --git a/frontend-tools/video-js/src/components/overlays/EndScreenOverlay.css b/frontend-tools/video-js/src/components/overlays/EndScreenOverlay.css index 6522639d..e9ef9896 100644 --- a/frontend-tools/video-js/src/components/overlays/EndScreenOverlay.css +++ b/frontend-tools/video-js/src/components/overlays/EndScreenOverlay.css @@ -5,16 +5,16 @@ top: 0; left: 0; width: 100%; - height: calc(100% - 100px); /* Leave space for seekbar and controls */ + height: calc(100% - 80px); /* Reduce reserved space for seekbar */ background: #000000; display: none; flex-direction: column; - justify-content: center; + justify-content: center; /* Center the grid vertically */ align-items: center; - padding: 20px; + padding: 40px 40px 40px 40px; /* Equal visual margins on all sides */ box-sizing: border-box; - z-index: 9999; /* Maximum z-index to cover absolutely everything */ - overflow: hidden; /* No scrollbar, fit content within available space */ + z-index: 9999; + overflow: hidden; } /* Hide poster image when video ends and end screen is shown */ @@ -46,15 +46,54 @@ top: 0 !important; left: 0 !important; width: 100vw !important; - height: 100vh !important; - z-index: 99999 !important; + height: calc(100vh - 80px) !important; /* Reduce reserved space for controls */ + z-index: 9998 !important; /* Below controls but above video */ display: flex !important; - padding-top: 80px !important; /* Add top padding to avoid covering title/avatar */ + padding: 120px 40px 40px 40px !important; /* Top padding for embed info + equal visual margins */ + justify-content: center !important; /* Center the grid vertically */ +} + +/* Small embed height optimization - 2 items horizontally for better title readability */ +@media (max-height: 500px) { + #page-embed .video-js-root-embed .vjs-related-videos-grid { + grid-template-columns: repeat(2, 1fr) !important; + grid-template-rows: 1fr !important; + gap: 20px !important; + max-width: 600px; /* Limit width for better proportions */ + } + + #page-embed .video-js-root-embed .video-js.vjs-ended .vjs-end-screen-overlay { + height: calc(100vh - 60px) !important; + padding: 80px 30px 30px 30px !important; + } + + /* Hide items beyond the first 2 */ + #page-embed .video-js-root-embed .vjs-related-video-item:nth-child(n + 3) { + display: none !important; + } +} + +/* Very small embed height - further optimize spacing */ +@media (max-height: 400px) { + #page-embed .video-js-root-embed .vjs-related-videos-grid { + gap: 15px !important; + max-width: 500px; + } + + #page-embed .video-js-root-embed .video-js.vjs-ended .vjs-end-screen-overlay { + height: calc(100vh - 50px) !important; + padding: 60px 25px 25px 25px !important; + } + + /* Ensure only 2 items and optimize their size */ + #page-embed .video-js-root-embed .vjs-related-video-item { + min-height: 80px !important; + } } /* Ensure controls stay visible over the black background */ .video-js.vjs-ended .vjs-control-bar { - z-index: 100000 !important; + z-index: 10000 !important; position: absolute !important; bottom: 0 !important; left: 0 !important; @@ -66,9 +105,9 @@ } .video-js.vjs-ended .vjs-progress-control { - z-index: 0 !important; + z-index: 10001 !important; position: absolute !important; - /* bottom: 48px !important; */ + bottom: 48px !important; left: 0 !important; right: 0 !important; width: 100% !important; @@ -84,7 +123,7 @@ left: 0 !important; right: 0 !important; width: 100vw !important; - z-index: 100000 !important; + z-index: 10000 !important; display: flex !important; opacity: 1 !important; visibility: visible !important; @@ -96,7 +135,7 @@ left: 0 !important; right: 0 !important; width: 100vw !important; - z-index: 100000 !important; + z-index: 10001 !important; display: block !important; opacity: 1 !important; visibility: visible !important; @@ -104,7 +143,7 @@ /* Ensure embed info overlay (title/avatar) stays visible when ended */ #page-embed .video-js-root-embed .video-js.vjs-ended .vjs-embed-info-overlay { - z-index: 100001 !important; + z-index: 10002 !important; display: flex !important; opacity: 1 !important; visibility: visible !important; @@ -182,21 +221,17 @@ .vjs-related-videos-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 2px; + gap: 16px; width: 100%; max-width: 100%; - margin: 0; + margin: 0; /* Remove margin since parent handles centering */ box-sizing: border-box; justify-items: stretch; align-items: stretch; justify-content: center; - align-content: start; + align-content: center; /* Center grid content */ overflow: hidden; - height: 100%; - flex: 1; /* Fill available space */ - grid-row-gap: 2px; - grid-column-gap: 2px; - grid-auto-rows: 1fr; /* Make all rows equal height */ + grid-auto-rows: 1fr; } .vjs-related-video-item { @@ -259,6 +294,11 @@ opacity: 1; } +/* Show overlay by default on touch devices - match default hover behavior exactly */ +.vjs-related-video-item.vjs-touch-device .vjs-related-video-overlay { + opacity: 1; +} + .vjs-related-video-title { font-size: 14px; font-weight: bold; @@ -312,6 +352,11 @@ opacity: 1; } +/* Show duration by default on touch devices */ +.vjs-related-video-item.vjs-touch-device .vjs-related-video-duration { + opacity: 1; +} + .video-js.vjs-ended .vjs-control-bar { opacity: 1 !important; pointer-events: auto !important; @@ -341,72 +386,78 @@ @media (max-width: 1200px) { .vjs-related-videos-grid { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - gap: 2px; - grid-row-gap: 2px; - grid-column-gap: 2px; - grid-auto-rows: 1fr; + gap: 14px; } .vjs-end-screen-overlay { - padding: 15px; - height: calc(100% - 95px); + height: calc(100% - 70px); + padding: 35px; + } + + #page-embed .video-js-root-embed .video-js.vjs-ended .vjs-end-screen-overlay { + height: calc(100vh - 70px) !important; + padding: 115px 35px 35px 35px !important; } } @media (max-width: 900px) { .vjs-related-videos-grid { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); - gap: 2px; - grid-row-gap: 2px; - grid-column-gap: 2px; - grid-auto-rows: 1fr; + gap: 12px; } .vjs-end-screen-overlay { - padding: 10px; - height: calc(100% - 90px); + height: calc(100% - 60px); + padding: 30px; + } + + #page-embed .video-js-root-embed .video-js.vjs-ended .vjs-end-screen-overlay { + height: calc(100vh - 60px) !important; + padding: 110px 30px 30px 30px !important; } } @media (max-width: 600px) { .vjs-related-videos-grid { grid-template-columns: repeat(2, 1fr); - gap: 2px; - grid-row-gap: 2px; - grid-column-gap: 2px; - grid-auto-rows: 1fr; + gap: 10px; } .vjs-end-screen-overlay { - padding: 5px; - height: calc(100% - 60px); - justify-content: flex-start; - padding-top: 10px; + height: calc(100% - 50px); + padding: 25px; + justify-content: center; + } + + #page-embed .video-js-root-embed .video-js.vjs-ended .vjs-end-screen-overlay { + height: calc(100vh - 50px) !important; + padding: 105px 25px 25px 25px !important; } .vjs-related-video-item { - min-height: 60px; + min-height: 80px; } } @media (max-width: 400px) { .vjs-related-videos-grid { grid-template-columns: repeat(2, 1fr); - gap: 2px; - grid-row-gap: 2px; - grid-column-gap: 2px; - grid-auto-rows: 1fr; + gap: 8px; } .vjs-end-screen-overlay { - height: calc(100% - 60px); - padding: 5px; - justify-content: flex-start; - padding-top: 10px; + height: calc(100% - 40px); + padding: 20px; + justify-content: center; + } + + #page-embed .video-js-root-embed .video-js.vjs-ended .vjs-end-screen-overlay { + height: calc(100vh - 40px) !important; + padding: 100px 20px 20px 20px !important; } .vjs-related-video-item { - min-height: 50px; + min-height: 70px; } } @@ -431,8 +482,16 @@ .vjs-related-videos-grid { grid-template-columns: repeat(4, 1fr); gap: 20px; - height: auto; - max-height: none; + } + + .vjs-end-screen-overlay { + height: calc(100% - 80px); + padding: 40px; + } + + #page-embed .video-js-root-embed .video-js.vjs-ended .vjs-end-screen-overlay { + height: calc(100vh - 80px) !important; + padding: 120px 40px 40px 40px !important; } } @@ -454,8 +513,6 @@ .vjs-related-videos-grid { grid-template-columns: repeat(3, 1fr); gap: 16px; - height: auto; - max-height: none; } /* Allow up to 9 videos on larger tablets */ @@ -469,8 +526,6 @@ .vjs-related-videos-grid { grid-template-columns: repeat(3, 1fr); gap: 16px; - height: auto; - max-height: none; } /* Allow up to 9 videos on large tablets */ @@ -483,15 +538,17 @@ .vjs-related-videos-grid { grid-template-columns: repeat(3, 1fr); gap: 14px; - height: auto; - max-height: none; } .vjs-end-screen-overlay { - padding: 16px; + height: calc(100% - 60px); + padding: 30px; justify-content: center; - padding-top: 20px; - padding-bottom: 20px; + } + + #page-embed .video-js-root-embed .video-js.vjs-ended .vjs-end-screen-overlay { + height: calc(100vh - 60px) !important; + padding: 110px 30px 30px 30px !important; } /* Allow up to 9 videos on regular tablets */ @@ -508,20 +565,18 @@ .vjs-related-videos-grid { grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(2, 1fr); - gap: 16px !important; - grid-gap: 16px !important; - max-height: 70vh; + gap: 12px; } .vjs-end-screen-overlay { padding: 12px; - justify-content: flex-start; - padding-top: 20px; - padding-bottom: 20px; + justify-content: center; + height: calc(100% - 105px); } - .vjs-related-video-item:nth-child(n + 5) { - display: none; + #page-embed .video-js-root-embed .video-js.vjs-ended .vjs-end-screen-overlay { + height: calc(100vh - 105px) !important; + padding: 80px 12px 12px 12px !important; } .vjs-related-video-thumbnail { @@ -537,16 +592,18 @@ .vjs-related-videos-grid { grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(2, 1fr); - gap: 14px !important; - grid-gap: 14px !important; - max-height: 65vh; + gap: 10px; } .vjs-end-screen-overlay { padding: 10px; - justify-content: flex-start; - padding-top: 15px; - padding-bottom: 15px; + justify-content: center; + height: calc(100% - 100px); + } + + #page-embed .video-js-root-embed .video-js.vjs-ended .vjs-end-screen-overlay { + height: calc(100vh - 100px) !important; + padding: 80px 10px 10px 10px !important; } } @@ -557,16 +614,18 @@ .vjs-related-videos-grid { grid-template-columns: repeat(2, 1fr); grid-template-rows: repeat(2, 1fr); - gap: 12px !important; - grid-gap: 12px !important; - max-height: 60vh; + gap: 8px; } .vjs-end-screen-overlay { padding: 8px; - justify-content: flex-start; - padding-top: 10px; - padding-bottom: 10px; + justify-content: center; + height: calc(100% - 98px); + } + + #page-embed .video-js-root-embed .video-js.vjs-ended .vjs-end-screen-overlay { + height: calc(100vh - 98px) !important; + padding: 80px 8px 8px 8px !important; } } diff --git a/frontend-tools/video-js/src/components/overlays/EndScreenOverlay.js b/frontend-tools/video-js/src/components/overlays/EndScreenOverlay.js index 590e0dd8..3cf02c24 100644 --- a/frontend-tools/video-js/src/components/overlays/EndScreenOverlay.js +++ b/frontend-tools/video-js/src/components/overlays/EndScreenOverlay.js @@ -55,8 +55,11 @@ class EndScreenOverlay extends Component { } createVideoItem(video) { + // Detect touch device + const isTouchDevice = this.isTouchDevice(); + const item = videojs.dom.createEl('div', { - className: 'vjs-related-video-item', + className: isTouchDevice ? 'vjs-related-video-item vjs-touch-device' : 'vjs-related-video-item', }); // Use real YouTube thumbnail or fallback to placeholder @@ -205,6 +208,19 @@ class EndScreenOverlay extends Component { getMaxVideosForScreen() { const width = window.innerWidth; + const height = window.innerHeight; + + // Check if this is an embed player + const playerId = this.player().id() || this.player().options_.id; + const isEmbedPlayer = + playerId === 'video-embed' || + document.getElementById('page-embed') || + window.location.pathname.includes('embed'); + + // For embed players with small height, limit to 2 items for better readability + if (isEmbedPlayer && height <= 500) { + return 2; // 2x1 grid for small embed heights + } if (width >= 1200) { return 12; // 4x3 grid for large desktop @@ -318,6 +334,16 @@ class EndScreenOverlay extends Component { ]; } + isTouchDevice() { + // Multiple methods to detect touch devices + return ( + 'ontouchstart' in window || + navigator.maxTouchPoints > 0 || + navigator.msMaxTouchPoints > 0 || + window.matchMedia('(pointer: coarse)').matches + ); + } + show() { this.el().style.display = 'flex'; }