Mischief

55 / Agent UI

Video Player

Video with captions that can be turned on, a real scrubber, and controls that survive full screen.

0:00 / 0:00

Installation

Copy the source into your project, or keep it behind a package.

npx shadcn@latest add Tinkerers-Labs/mischief-ui/video-player
import { VideoPlayer } from "mischief-ui/video-player"
Also installs
  • lucide-react

Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.

registry/default/video-player/video-player.tsx
"use client" import * as React from "react"import {  Captions,  CaptionsOff,  Maximize,  Minimize,  Pause,  Play,  Volume2,  VolumeX,} from "lucide-react" 

Usage

export function Recording({ src, captions }) {
  return <VideoPlayer src={src} label="the walkthrough" tracks={captions} />
}

Captions are a track, not a transcript

A caption belongs to a moment in the video, which is what WebVTT and the track element already express. Rendering your own list of lines beside the picture is a different thing, useful for reading and useless for watching.

<VideoPlayer
  src="/walkthrough.mp4"
  label="the walkthrough"
  tracks={[{ src: "/walkthrough.vtt", srcLang: "en", label: "English", default: true }]}
/>

Taking the browser's controls away takes its caption menu with them, so the track mode is set here by hand. The browser still draws the cues over the picture once a track is showing, which is why the toggle is three lines rather than a caption renderer.

Owning the controls means owning all of them

Play, mute, speed, captions and full screen are each a real button with a name that says which way it will go, and the pressed ones carry aria-pressed. Seeking is a range input, so arrow keys, Home and End work and the position is announced as a time rather than a count of seconds.

Full screen is requested on the whole component rather than on the video, so the controls come along instead of being replaced by the browser's own.

API

srcstringThe video.
labelstringNames the video in every control's label.
posterstringShown before playback starts.
tracksreadonly VideoTrack[]Caption and subtitle tracks.
ratesreadonly number[]Speeds the button cycles through. Defaults to 1, 1.5 and 2.
...rootPropsHTMLAttributes<HTMLDivElement>Native root attributes.

VideoTrack

srcstringThe WebVTT file.
srcLangstringA BCP 47 tag, such as "en".
labelstringHow the track is named to a reader.
kind"captions" | "subtitles"Captions carry sound, subtitles carry speech. Defaults to captions.
defaultbooleanStart with this one showing.

Accessibility

The video carries an accessible name, and every control is a button whose name says the action it will take rather than the state it is in. Toggles carry aria-pressed. Seeking is a native range input announced as a position in minutes and seconds, so it works from a keyboard. Captions are real tracks, which means the browser draws them, the reader can style them in their own settings, and a deaf viewer gets them without a transcript being bolted on beside the picture. Full screen is entered on the component so the controls remain.