Video Player
Custom video player with progress bar, playback controls, volume, speed selector, fullscreen support, and poster overlay.
Basic Video Player
Preview
Video Content
1:24
/
4:52
<div class="video-player video-player-16-9" style="max-width: 600px;">
<div class="video-player-video" style="position: absolute; inset: 0; background: #111; display: flex; align-items: center; justify-content: center;">
<span style="color: rgba(255,255,255,0.3);">Video Content</span>
</div>
<div class="video-player-controls" style="opacity: 1; visibility: visible;">
<div class="video-player-progress-wrapper">
<div class="video-player-progress">
<div class="video-player-progress-buffer" style="width: 60%;"></div>
<div class="video-player-progress-fill" style="width: 30%;"></div>
</div>
<div class="video-player-progress-thumb" style="left: 30%; transform: translate(-50%, -50%) scale(1);"></div>
</div>
<div class="video-player-controls-bar">
<div class="video-player-controls-left">
<button class="video-player-btn" aria-label="Play">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>
</button>
<div class="video-player-time">
<span>1:24</span>
<span class="video-player-time-separator">/</span>
<span>4:52</span>
</div>
</div>
<div class="video-player-controls-center"></div>
<div class="video-player-controls-right">
<div class="video-player-volume">
<button class="video-player-btn video-player-btn-sm" aria-label="Volume">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>
</button>
</div>
<button class="video-player-btn video-player-btn-sm" aria-label="Fullscreen">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/><line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/></svg>
</button>
</div>
</div>
</div>
</div> Requires: ux.min.css
<div class="relative w-full overflow-hidden rounded-box bg-black select-none" style="max-width: 600px; aspect-ratio: 16/9;">
<div class="absolute inset-0 bg-neutral-900 flex items-center justify-center">
<span class="text-white/30">Video Content</span>
</div>
<div class="absolute bottom-0 left-0 right-0 flex flex-col px-4 pb-2 pt-6" style="background: linear-gradient(transparent, rgba(0,0,0,0.7));">
<div class="relative w-full flex items-center h-5 cursor-pointer">
<div class="relative w-full h-1 bg-white/30 rounded-sm overflow-hidden">
<div class="absolute top-0 left-0 h-full bg-white/50 rounded-sm" style="width: 60%;"></div>
<div class="absolute top-0 left-0 h-full bg-primary rounded-sm" style="width: 30%;"></div>
</div>
</div>
<div class="flex items-center gap-1 mt-1">
<button class="flex items-center justify-center w-11 h-11 bg-transparent border-none rounded-lg text-white cursor-pointer hover:bg-white/10">
<svg class="size-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>
</button>
<span class="text-sm font-medium text-white" style="font-variant-numeric: tabular-nums;">1:24 <span class="opacity-60">/</span> 4:52</span>
<div class="flex-1"></div>
<button class="flex items-center justify-center w-9 h-9 bg-transparent border-none rounded-lg text-white cursor-pointer hover:bg-white/10">
<svg class="size-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/><line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/></svg>
</button>
</div>
</div>
</div> Requires: tw.min.css
// Wire up HTML5 video element:
// const video = document.querySelector('video');
// playBtn.onclick = () => video.paused ? video.play() : video.pause();
// video.ontimeupdate = () => {
// progressFill.style.width = (video.currentTime / video.duration * 100) + '%';
// };
// fullscreenBtn.onclick = () => video.requestFullscreen(); With Poster Overlay
Preview
<div class="video-player video-player-16-9" style="max-width: 600px;">
<div class="video-player-video" style="position: absolute; inset: 0; background: #111;"></div>
<div class="video-player-poster" style="background-image: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
<button class="video-player-poster-play" aria-label="Play video">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>
</button>
</div>
</div> Requires: ux.min.css
<div class="relative w-full overflow-hidden rounded-box bg-black select-none" style="max-width: 600px; aspect-ratio: 16/9;">
<div class="absolute inset-0 bg-neutral-900"></div>
<div class="absolute inset-0 flex items-center justify-center cursor-pointer" style="background: linear-gradient(135deg, #667eea, #764ba2);">
<button class="flex items-center justify-center w-[72px] h-[72px] rounded-full border-2 border-white/30 bg-black/60 text-white cursor-pointer backdrop-blur-[10px] hover:scale-110 active:scale-95" style="transition: transform 0.15s;">
<svg class="size-8 ml-1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"/></svg>
</button>
</div>
</div> Requires: tw.min.css
// Hide poster on first play:
// posterPlay.onclick = () => {
// video.play();
// poster.classList.add('video-player-poster-hidden');
// }; Loading State
Preview
<div class="video-player video-player-16-9 video-player-is-loading" style="max-width: 600px;">
<div class="video-player-video" style="position: absolute; inset: 0; background: #111;"></div>
<div class="video-player-loading" style="opacity: 1; visibility: visible;">
<div class="video-player-spinner"></div>
</div>
</div> Requires: ux.min.css
<div class="relative w-full overflow-hidden rounded-box bg-black select-none" style="max-width: 600px; aspect-ratio: 16/9;">
<div class="absolute inset-0 bg-neutral-900"></div>
<div class="absolute inset-0 flex items-center justify-center bg-black/40">
<div class="w-12 h-12 rounded-full border-3 border-white/30 border-t-white animate-spin"></div>
</div>
</div> Requires: tw.min.css
// Show loading during buffering:
// video.onwaiting = () => player.classList.add('video-player-is-loading');
// video.onplaying = () => player.classList.remove('video-player-is-loading');