Audio player
A tune with a waveform to scrub, its time, speed and mute, a one-line row variant for lists, and loading and unavailable states — with no request at all when there is no file.
An <audio> the browser plays, drawn as the kit draws it: Play named for the tune ("Play {title}"), the waveform from peaks — one bar per slice of loudness, the played ones filled in the accent and the rest in the field grey — which is also the slider that seeks it ("Seek {title}"), the time, a speed menu from 0.5× to 2× and a mute. Without peaks the waveform is a plain track. The arrows on the waveform move five seconds in reading order (in a right-to-left page it fills from the right and ArrowLeft goes forward), Home and End go to the ends, and the pointer seeks where it presses. One kit audio player plays at a time: starting one pauses whichever other is playing, on the same page. durationHint is printed until the file says its own length, so a list can show every row's length without loading every file — pair it with preload="none"; with neither, only the time played is shown. variant="row" puts Play, the waveform and the time on one line for a list, with no title line, speed or mute: the row's own text names it. A Loading audio spinner shows while the reader waits after pressing Play. A file it cannot play says "Audio unavailable" and why in place of the waveform — a dropped connection, or, since a bare src declares no type and a 404 looks like a format the browser cannot play, that the file isn't there or can't be played here — as an alert, and Play is held (still focusable, so the reason is read with it as its description); the focus on a control it drops moves to Play. With no src at all the player renders no media element and makes no request; it says unavailableReason ("Audio unavailable" unless you say otherwise) the same way, but quietly, since nothing failed, and shows durationHint if it has one. Where a row is too narrow for the whole reason, say a short one in unavailableReason and the whole one in unavailableDetail: the line beside Play stays one a row can hold, and Play reads the whole reason as its description. The time is a run of its own, left to right, so a right-to-left page keeps "0:05 / 0:16" in order. The shadcn CLI installs text only, so audio files are not installed with the player: put them in public/ and point src at them, and an install without them shows the unavailable state.
Install
npx shadcn@latest add @vibra/audio-playerNeeds the @vibra registry in your components.json — set it up once.