Video Player
A framed video player with hover-revealed custom transport controls.
- Status
- Since
0.5.0- Accessibility pattern
- native media controls
Last updated
Install
Add Video Player from the VegaStack registry. The CLI verifies the item's integrity hash before writing it.
pnpm dlx shadcn@latest add @vegastack/video-playerThe same command installs the registry items it composes: @vegastack/media-player-controls.
Usage
import { VideoPlayer } from "@/components/ui/video-player";
<VideoPlayer
src="/media/demo.mp4"
poster="/media/demo-poster.webp"
label="Product demo video"
/>;Examples
Basic
The video surface owns a hover-revealed overlay transport: a thin media progress rail that expands smoothly on hover or focus, then play/pause, time, volume, settings, and fullscreen in the lower row. The docs preview uses a 90-second local MP4 fixture, so it can play without network access.
With copy
Use title and description when a video needs a visible name or supporting context.
Aspect ratio
aspectRatio="video" reserves a 16:9 frame. Use square for square media or auto
when the video element should keep its intrinsic height.
Controls always visible
The overlay reveals on pointer or focus and fades a second after the pointer leaves. controlsVisible
takes that out of the loop — true pins the chrome open, false keeps it closed. Use it for a
kiosk or always-on player, and for a static fixture: the auto-reveal never fires without a pointer,
so a screenshot or a contract check would otherwise capture a frame with no controls in it.
Playground
Every VideoPlayer prop, auto-generated from its TypeScript types.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
src* | string | — | Video source URL. |
aspectRatio | "auto" | "square" | "video" | 'video' | Reserved frame aspect ratio. |
className | string | — | Classes applied to the outer player container. |
controlsVisible | boolean | — | Force the overlay controls open (true) or closed (false), taking the
auto hide/reveal out of the loop. Leave it undefined for the default
behaviour: reveal on pointer or focus, fade out a second after the pointer
leaves. Use true for a kiosk/always-on player — and for a static docs or
test fixture, which is what lets the contract lane see the chrome at all. |
defaultPlaybackRate | number | 1 | Initial playback rate applied when the video element mounts. |
defaultQuality | string | '720p' | Initial quality label selected in the settings menu. |
description | React.ReactNode | — | Optional visible description shown below the title. |
formatTime | ((seconds: number) => string) | mm:ss / h:mm:ss | Format elapsed and duration labels. |
label | string | 'Video' | Accessible label used by the video element and custom controls. |
mediaRef | React.Ref<HTMLVideoElement> | — | Ref for the native <video> media engine. |
onPlaybackRateChange | ((playbackRate: number) => void) | — | Called whenever the playback rate changes. |
onPlayStateChange | ((playing: boolean) => void) | — | Called whenever playback starts or pauses. |
onQualityChange | ((quality: string) => void) | — | Called whenever the selected quality label changes. |
onTimeChange | ((currentTime: number, duration: number) => void) | — | Called whenever the current playback time changes. |
playbackRates | readonly number[] | [0.75, 1, 1.25, 1.5, 2] | Playback rates cycled by the rate control. |
qualityOptions | readonly string[] | ['144p', '240p', '360p', '480p', '720p', '1080p'] | Selectable quality labels shown in the settings menu. |
ref | React.Ref<HTMLDivElement> | — | Ref for the outer player container. |
skipSeconds | number | 15 | Seconds moved by the rewind and forward actions. |
title | React.ReactNode | — | Optional visible title shown above the video frame. |
videoClassName | string | — | Classes applied to the native <video> frame. |
Data attributes and CSS variables on VideoPlayer
| Attribute | Values |
|---|---|
data-aspect-ratio | mirrors a prop or state value |
data-slot | "video-player" | "video-player-controls-overlay" | "video-player-controls-scrim" | "video-player-frame" | "video-player-header" | "video-player-media" |
data-state | "hidden" | "visible" |
Accessibility
| Key | Action |
|---|---|
| Tab | Move through play/pause, seek, mute, settings, and fullscreen |
| Enter / Space | Activate the focused button |
| Space | Play or pause when the player or media controls group is focused |
| K | Play or pause |
| ← | Rewind 15 seconds when the player or media controls group is focused |
| → | Forward 15 seconds when the player or media controls group is focused |
| J | Rewind 15 seconds |
| L | Forward 15 seconds |
| M | Toggle mute |
| F | Toggle fullscreen when the player is focused |
| ↑ / ↓ | Change volume while the volume slider is focused |
| ← / ↓ | Decrease the seek position by one second while the slider is focused |
| → / ↑ | Increase the seek position by one second while the slider is focused |
| Home | Seek to the beginning |
| End | Seek to the end |
- Pass a descriptive
label; it names the video element and prefixes every custom control. - The named media progress rail composes
Slider, so keyboard and screen-reader slider semantics stay intact while its video-specific hover treatment remains separate from the generic component. - Icon-only transport buttons are composed with
IconButton, which requires accessible names. - Tooltips expose the primary keyboard shortcuts for pointer and keyboard users.
- The volume slider is exposed from the mute control and keeps slider keyboard semantics.
- Playback speed and quality live in settings submenus with radio selection.
- Rewind and forward are keyboard shortcuts rather than visible buttons.
- The overlay controls appear on hover or focus and fade out after the pointer leaves the frame.
- The fullscreen action changes to an exit-fullscreen icon and accessible name while the frame owns document fullscreen.
- The native
<video>element is the media engine and visual frame; browser-native controls are omitted so audio and video players share the same VegaStack transport.
| Contract | States tested |
|---|---|
| Behaviour | default, playing, paused, muted, seeking, keyboard-shortcuts, playback-rate, quality, volume, fullscreen, fullscreen-exit, hover-fade-controls |
| Accessibility | focus-visible, keyboard-operable, labeled, semantic-html |
| Visual | default, with-copy, aspect-ratio, progress-rest, progress-hover, volume-open |