Skip to content
Component installs need the registry setup— the Base UI shadcn project, the @vegastack namespace and the Cloudflare Access service token.
VegaStack Design

Video Player

A framed video player with hover-revealed custom transport controls.

Status
stable
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-player

The 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.

Product walkthrough
A video frame using the same transport as the audio player.

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

PropTypeDefaultDescription
src*stringVideo source URL.
aspectRatio"auto" | "square" | "video"'video'Reserved frame aspect ratio.
classNamestringClasses applied to the outer player container.
controlsVisiblebooleanForce 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.
defaultPlaybackRatenumber1Initial playback rate applied when the video element mounts.
defaultQualitystring'720p'Initial quality label selected in the settings menu.
descriptionReact.ReactNodeOptional visible description shown below the title.
formatTime((seconds: number) => string)mm:ss / h:mm:ssFormat elapsed and duration labels.
labelstring'Video'Accessible label used by the video element and custom controls.
mediaRefReact.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.
playbackRatesreadonly number[][0.75, 1, 1.25, 1.5, 2]Playback rates cycled by the rate control.
qualityOptionsreadonly string[]['144p', '240p', '360p', '480p', '720p', '1080p']Selectable quality labels shown in the settings menu.
refReact.Ref<HTMLDivElement>Ref for the outer player container.
skipSecondsnumber15Seconds moved by the rewind and forward actions.
titleReact.ReactNodeOptional visible title shown above the video frame.
videoClassNamestringClasses applied to the native <video> frame.

Data attributes and CSS variables on VideoPlayer

AttributeValues
data-aspect-ratiomirrors 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

KeyAction
TabMove through play/pause, seek, mute, settings, and fullscreen
Enter / SpaceActivate the focused button
SpacePlay or pause when the player or media controls group is focused
KPlay 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
JRewind 15 seconds
LForward 15 seconds
MToggle mute
FToggle 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
HomeSeek to the beginning
EndSeek 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.
ContractStates tested
Behaviourdefault, playing, paused, muted, seeking, keyboard-shortcuts, playback-rate, quality, volume, fullscreen, fullscreen-exit, hover-fade-controls
Accessibilityfocus-visible, keyboard-operable, labeled, semantic-html
Visualdefault, with-copy, aspect-ratio, progress-rest, progress-hover, volume-open

Do / Don't

Do
Use VideoPlayer for a single video asset that needs a branded frame and standard transport controls.
Don't
Use VideoPlayer for playlists, caption editing, or multi-source adaptive streaming controls.

On this page