diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 5f9aadd..aa93a33 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -2,7 +2,8 @@ "permissions": { "allow": [ "Bash(find:*)", - "Bash(cat:*)" + "Bash(cat:*)", + "Bash(npm run build:*)" ], "deny": [], "ask": [] diff --git a/src/components/ControlsLayer.css b/src/components/ControlsLayer.css index dfeb8f8..8203462 100644 --- a/src/components/ControlsLayer.css +++ b/src/components/ControlsLayer.css @@ -1,80 +1,56 @@ /* ============================================ - CONTROLS LAYER - Overlay Container + CONTROLS LAYER + Modern, Clean & Minimal Design ============================================ */ .controls-layer { - /* Positioning */ position: absolute; inset: 0; - - /* Stacking */ z-index: var(--player-z-controls); - - /* Layout - Push controls to bottom */ display: flex; flex-direction: column; justify-content: flex-end; - - /* Interaction */ cursor: default; pointer-events: auto; - - /* Animation */ opacity: 1; transition: opacity var(--player-transition-normal) ease; } -/* ============================================ - VISIBILITY STATES - ============================================ */ - -/* Visible state (explicit for clarity) */ +/* Visible state */ .controls-layer.visible { opacity: 1; } -/* Hidden while playing */ +/* Hidden state */ .controls-layer.hidden.playing { opacity: 0; } -/* Hide cursor in fullscreen when controls are hidden */ +/* Hide cursor in fullscreen */ .controls-layer.fullscreen.hidden.playing { cursor: none; } /* ============================================ - CONTROLS BAR - Bottom Control Panel + CONTROLS BAR ============================================ */ .controls-bar { - /* Layout */ display: flex; flex-direction: column; - - /* Spacing - CRITICAL: Leave room for subtitles */ - padding-top: var(--player-spacing-xl); - padding-bottom: var(--player-spacing-lg); - padding-left: var(--player-spacing-lg); - padding-right: var(--player-spacing-lg); - - /* Background gradient */ + padding: var(--player-spacing-xl) var(--player-spacing-lg) var(--player-spacing-lg); background: linear-gradient( to top, var(--player-bg-controls) 0%, - var(--player-bg-controls) 60%, + var(--player-bg-controls) 50%, transparent 100% ); - - /* Animation */ transform: translateY(0); transition: transform var(--player-transition-normal) ease; - - /* Interaction */ pointer-events: auto; } -/* Slide controls down when hidden (allows clicks to pass through) */ +/* Slide controls down when hidden */ .controls-layer.hidden.playing .controls-bar { transform: translateY(100%); pointer-events: none; @@ -89,7 +65,7 @@ } /* ============================================ - CONTROLS ROW - Button Container + CONTROLS ROW ============================================ */ .controls-row { @@ -99,14 +75,12 @@ gap: var(--player-spacing-sm); } -/* Left side controls group */ .controls-left { display: flex; align-items: center; gap: var(--player-spacing-sm); } -/* Right side controls group */ .controls-right { display: flex; align-items: center; @@ -115,15 +89,12 @@ } /* ============================================ - MOBILE RESPONSIVE + RESPONSIVE ============================================ */ @media (max-width: 640px) { .controls-bar { - padding-top: var(--player-spacing-lg); - padding-bottom: var(--player-spacing-md); - padding-left: var(--player-spacing-md); - padding-right: var(--player-spacing-md); + padding: var(--player-spacing-lg) var(--player-spacing-md) var(--player-spacing-md); } .controls-row { diff --git a/src/components/VideoElement.css b/src/components/VideoElement.css index b210161..a1f3a82 100644 --- a/src/components/VideoElement.css +++ b/src/components/VideoElement.css @@ -1,5 +1,6 @@ /* ============================================ - VIDEO CONTAINER + VIDEO ELEMENT + Modern & Clean Design ============================================ */ .video-container { @@ -11,10 +12,6 @@ pointer-events: none; } -/* ============================================ - VIDEO ELEMENT - ============================================ */ - .video-element { width: 100%; height: 100%; @@ -24,143 +21,85 @@ } /* ============================================ - HIDE NATIVE MEDIA CONTROLS + HIDE NATIVE CONTROLS ============================================ */ -/* Chrome/Safari/Edge */ -.video-element::-webkit-media-controls { - display: none !important; -} - -.video-element::-webkit-media-controls-enclosure { - display: none !important; -} - +.video-element::-webkit-media-controls, +.video-element::-webkit-media-controls-enclosure, .video-element::-webkit-media-controls-panel { display: none !important; } -/* Firefox */ .video-element::-moz-media-controls { display: none !important; } /* ============================================ - SUBTITLE STYLING - Modern & Cross-Browser - Optimized for Chromium and Firefox + SUBTITLE STYLING - Clean & Modern + Cross-browser compatible ============================================ */ -/* ==================== BASE CUE STYLES ==================== */ - -/* All browsers - Base subtitle appearance */ +/* Base subtitle appearance */ ::cue { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif !important; - font-size: 1.5rem !important; - font-weight: 700 !important; - line-height: 1.5 !important; + font-size: 1.4rem !important; + font-weight: 600 !important; + line-height: 1.4 !important; color: #ffffff !important; - background-color: rgba(0, 0, 0, 0.9) !important; - padding: 0.4em 0.8em !important; - border-radius: 6px !important; - text-shadow: - 0 1px 2px rgba(0, 0, 0, 1), - 0 2px 8px rgba(0, 0, 0, 0.8) !important; + background-color: rgba(0, 0, 0, 0.85) !important; + padding: 0.3em 0.65em !important; + border-radius: 4px !important; + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8) !important; white-space: pre-line !important; } -/* Chromium specific */ .video-element::cue { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif !important; - font-size: 1.5rem !important; - font-weight: 700 !important; - line-height: 1.5 !important; + font-size: 1.4rem !important; + font-weight: 600 !important; + line-height: 1.4 !important; color: #ffffff !important; - background-color: rgba(0, 0, 0, 0.9) !important; - padding: 0.4em 0.8em !important; - border-radius: 6px !important; - text-shadow: - 0 1px 2px rgba(0, 0, 0, 1), - 0 2px 8px rgba(0, 0, 0, 0.8) !important; - white-space: pre-line !important; -} - -/* Firefox specific - Base styles */ -::-moz-cue { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif !important; - font-size: 1.5rem !important; - font-weight: 700 !important; - line-height: 1.5 !important; - color: #ffffff !important; - background-color: rgba(0, 0, 0, 0.9) !important; - padding: 0.4em 0.8em !important; - border-radius: 6px !important; - text-shadow: - 0 1px 2px rgba(0, 0, 0, 1), - 0 2px 8px rgba(0, 0, 0, 0.8) !important; - white-space: pre-line !important; -} - -video::-moz-cue { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif !important; - font-size: 1.5rem !important; - font-weight: 700 !important; - line-height: 1.5 !important; - color: #ffffff !important; - background-color: rgba(0, 0, 0, 0.9) !important; - padding: 0.4em 0.8em !important; - border-radius: 6px !important; - text-shadow: - 0 1px 2px rgba(0, 0, 0, 1), - 0 2px 8px rgba(0, 0, 0, 0.8) !important; + background-color: rgba(0, 0, 0, 0.85) !important; + padding: 0.3em 0.65em !important; + border-radius: 4px !important; + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8) !important; white-space: pre-line !important; } +/* Firefox specific */ +::-moz-cue, +video::-moz-cue, .video-element::-moz-cue { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, sans-serif !important; - font-size: 1.5rem !important; - font-weight: 700 !important; - line-height: 1.5 !important; + font-size: 1.4rem !important; + font-weight: 600 !important; + line-height: 1.4 !important; color: #ffffff !important; - background-color: rgba(0, 0, 0, 0.9) !important; - padding: 0.4em 0.8em !important; - border-radius: 6px !important; - text-shadow: - 0 1px 2px rgba(0, 0, 0, 1), - 0 2px 8px rgba(0, 0, 0, 0.8) !important; + background-color: rgba(0, 0, 0, 0.85) !important; + padding: 0.3em 0.65em !important; + border-radius: 4px !important; + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.8) !important; white-space: pre-line !important; } -/* ==================== TEXT FORMATTING ==================== */ - -::cue(b), ::cue(strong) { - font-weight: 900 !important; +/* Text formatting */ +::cue(b), ::cue(strong), +.video-element::cue(b), .video-element::cue(strong) { + font-weight: 700 !important; } -::cue(i), ::cue(em) { - font-style: italic !important; -} - -::cue(u) { - text-decoration: underline !important; -} - -.video-element::cue(b), -.video-element::cue(strong) { - font-weight: 900 !important; -} - -.video-element::cue(i), -.video-element::cue(em) { +::cue(i), ::cue(em), +.video-element::cue(i), .video-element::cue(em) { font-style: italic !important; } +::cue(u), .video-element::cue(u) { text-decoration: underline !important; } -/* Firefox text formatting */ ::-moz-cue(b), ::-moz-cue(strong) { - font-weight: 900 !important; + font-weight: 700 !important; } ::-moz-cue(i), ::-moz-cue(em) { @@ -171,114 +110,83 @@ video::-moz-cue { text-decoration: underline !important; } -/* ==================== CHROMIUM POSITIONING ==================== */ +/* ============================================ + SUBTITLE POSITIONING - Chromium + ============================================ */ -/* Container - Chromium */ .video-element::-webkit-media-text-track-container { position: absolute !important; - bottom: var(--player-subtitle-bottom-offset) !important; - bottom: calc(var(--player-subtitle-bottom-offset) + env(safe-area-inset-bottom, 0px)) !important; + bottom: var(--player-subtitle-bottom) !important; left: 0 !important; right: 0 !important; width: 100% !important; height: auto !important; - display: flex !important; flex-direction: column !important; align-items: center !important; justify-content: center !important; - - padding: 0 !important; + padding: 0 16px !important; margin: 0 !important; - overflow: visible !important; z-index: 10 !important; pointer-events: none !important; + transition: bottom 0.2s ease !important; +} + +/* When controls are hidden, move subtitles down */ +.video-player.controls-hidden .video-element::-webkit-media-text-track-container { + bottom: var(--player-subtitle-bottom-hidden) !important; } -/* Display wrapper - Chromium */ .video-element::-webkit-media-text-track-display { width: 100% !important; max-width: 100% !important; - display: flex !important; flex-direction: column !important; align-items: center !important; justify-content: center !important; - margin: 0 !important; - padding: 0 20px !important; - + padding: 0 !important; text-align: center !important; } -/* ==================== FIREFOX POSITIONING ==================== */ - -/* Firefox - Position subtitles using video wrapper approach */ -@-moz-document url-prefix() { - .video-container { - position: relative; - } - - .video-element { - position: relative; - } - - /* Firefox subtitle container positioning */ - .video-element::cue { - position: relative; - } -} - -/* Firefox - Use text track display positioning */ -video::-moz-text-track-display { - position: absolute !important; - bottom: var(--player-subtitle-bottom-offset) !important; - bottom: calc(var(--player-subtitle-bottom-offset) + env(safe-area-inset-bottom, 0px)) !important; - left: 0 !important; - right: 0 !important; - width: 100% !important; - - display: flex !important; - flex-direction: column !important; - align-items: center !important; - justify-content: center !important; - - padding: 0 20px !important; - margin: 0 !important; - - text-align: center !important; - z-index: 10 !important; - pointer-events: none !important; -} +/* ============================================ + SUBTITLE POSITIONING - Firefox + ============================================ */ +video::-moz-text-track-display, .video-element::-moz-text-track-display { position: absolute !important; - bottom: var(--player-subtitle-bottom-offset) !important; - bottom: calc(var(--player-subtitle-bottom-offset) + env(safe-area-inset-bottom, 0px)) !important; + bottom: var(--player-subtitle-bottom) !important; left: 0 !important; right: 0 !important; width: 100% !important; - display: flex !important; flex-direction: column !important; align-items: center !important; justify-content: center !important; - - padding: 0 20px !important; + padding: 0 16px !important; margin: 0 !important; - text-align: center !important; z-index: 10 !important; pointer-events: none !important; + transition: bottom 0.2s ease !important; } -/* ==================== FULLSCREEN ==================== */ +/* When controls are hidden, move subtitles down */ +.video-player.controls-hidden video::-moz-text-track-display, +.video-player.controls-hidden .video-element::-moz-text-track-display { + bottom: var(--player-subtitle-bottom-hidden) !important; +} + +/* ============================================ + FULLSCREEN + ============================================ */ .video-element:fullscreen::cue, :fullscreen .video-element::cue { - font-size: 2rem !important; - padding: 0.5em 1em !important; + font-size: 1.8rem !important; + padding: 0.4em 0.8em !important; } ::-moz-cue:fullscreen, @@ -287,37 +195,37 @@ video:fullscreen::-moz-cue, :fullscreen ::-moz-cue, :fullscreen video::-moz-cue, :fullscreen .video-element::-moz-cue { - font-size: 2rem !important; - padding: 0.5em 1em !important; + font-size: 1.8rem !important; + padding: 0.4em 0.8em !important; } .video-element:fullscreen::-webkit-media-text-track-container, :fullscreen .video-element::-webkit-media-text-track-container { - bottom: calc(var(--player-subtitle-bottom-offset) + 40px) !important; - bottom: calc(var(--player-subtitle-bottom-offset) + 40px + env(safe-area-inset-bottom, 0px)) !important; + bottom: calc(var(--player-subtitle-bottom) + 20px) !important; } video:fullscreen::-moz-text-track-display, .video-element:fullscreen::-moz-text-track-display, :fullscreen video::-moz-text-track-display, :fullscreen .video-element::-moz-text-track-display { - bottom: calc(var(--player-subtitle-bottom-offset) + 40px) !important; - bottom: calc(var(--player-subtitle-bottom-offset) + 40px + env(safe-area-inset-bottom, 0px)) !important; + bottom: calc(var(--player-subtitle-bottom) + 20px) !important; } -/* ==================== RESPONSIVE ==================== */ +/* ============================================ + RESPONSIVE + ============================================ */ -@media (max-width: 1024px) { +@media (max-width: 768px) { ::cue, .video-element::cue, ::-moz-cue, video::-moz-cue, .video-element::-moz-cue { - font-size: 1.35rem !important; + font-size: 1.25rem !important; } .video-player { - --player-subtitle-bottom-offset: clamp(80px, 14vh, 120px); + --player-subtitle-bottom: 85px; } } @@ -327,22 +235,18 @@ video:fullscreen::-moz-text-track-display, ::-moz-cue, video::-moz-cue, .video-element::-moz-cue { - font-size: 1.15rem !important; - padding: 0.35em 0.7em !important; + font-size: 1.1rem !important; + padding: 0.25em 0.55em !important; } .video-player { - --player-subtitle-bottom-offset: 80px; + --player-subtitle-bottom: 75px; } .video-element::-webkit-media-text-track-container { padding: 0 12px !important; } - .video-element::-webkit-media-text-track-display { - max-width: 92% !important; - } - video::-moz-text-track-display, .video-element::-moz-text-track-display { padding: 0 12px !important; @@ -359,6 +263,6 @@ video:fullscreen::-moz-text-track-display, } .video-player { - --player-subtitle-bottom-offset: 60px; + --player-subtitle-bottom: 65px; } } diff --git a/src/components/VideoPlayer.css b/src/components/VideoPlayer.css index 9896a92..276e33f 100644 --- a/src/components/VideoPlayer.css +++ b/src/components/VideoPlayer.css @@ -1,43 +1,56 @@ /* ============================================ VIDEO PLAYER - Main Container + Modern, Clean & Minimal Design ============================================ */ .video-player { + /* Positioning */ position: relative; width: 100%; max-width: 100%; + + /* Appearance */ background-color: var(--player-bg); + border-radius: var(--player-radius); overflow: hidden; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', - 'Droid Sans', 'Helvetica Neue', sans-serif; + + /* Typography */ + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + + /* Interaction */ user-select: none; -webkit-user-select: none; + + /* Isolation for library usage */ + contain: layout style paint; } -/* Apply box-sizing to all child elements */ -.video-player * { +/* Box model consistency */ +.video-player *, +.video-player *::before, +.video-player *::after { box-sizing: border-box; + margin: 0; + padding: 0; } -/* Reset video element box-sizing to preserve native subtitle rendering */ +/* Preserve video native rendering */ .video-player video { box-sizing: content-box; } /* ============================================ - ASPECT RATIO - 16:9 Intrinsic Ratio + ASPECT RATIO - 16:9 ============================================ */ -/* Use padding-top hack for aspect ratio */ .video-player::before { content: ''; display: block; - padding-top: 56.25%; /* 16:9 aspect ratio */ + padding-top: 56.25%; } -/* Position all direct children absolutely within aspect ratio container */ .video-player > * { position: absolute; top: 0; @@ -47,31 +60,20 @@ } /* ============================================ - FULLSCREEN SUPPORT + FULLSCREEN - Remove border radius ============================================ */ -.video-player:fullscreen { - width: 100vw; - height: 100vh; -} - -.video-player:-webkit-full-screen { - width: 100vw; - height: 100vh; -} - -.video-player:-moz-full-screen { - width: 100vw; - height: 100vh; -} - +.video-player:fullscreen, +.video-player:-webkit-full-screen, +.video-player:-moz-full-screen, .video-player:-ms-fullscreen { width: 100vw; height: 100vh; + border-radius: 0; } /* ============================================ - NATIVE CONTROLS - Hide completely + HIDE NATIVE CONTROLS ============================================ */ .video-player video::-webkit-media-controls { @@ -86,7 +88,7 @@ display: none !important; } -/* Ensure subtitle text tracks are visible */ +/* Keep subtitles visible */ .video-player video::-webkit-media-text-track-container, .video-player video::-webkit-media-text-track-display { display: block !important; diff --git a/src/components/VideoPlayer.tsx b/src/components/VideoPlayer.tsx index 262a30b..5af7421 100644 --- a/src/components/VideoPlayer.tsx +++ b/src/components/VideoPlayer.tsx @@ -58,10 +58,11 @@ const VideoPlayerContent: React.FC< qualities, onQualityLevelsLoadedInternal, }) => { - const { containerRef } = usePlayerContext() + const { containerRef, uiState } = usePlayerContext() + const controlsHiddenClass = !uiState.controlsVisible ? 'controls-hidden' : '' return ( -
+