Music Player
A fully accessible HTML5 audio player component with transport controls, volume, optional shuffle and repeat modes, seek bar, playlist, glass surface, and a detachable floating mode (corner placement, pointer-drag reposition, minimizable). Zero external dependencies, powered by the native Audio API, and it automatically adapts to the active Vanduo theme.
Install the player separately from the core framework. All live demos below use a bundled sample playlist from Invent the Universe, with Pale Blue Dot as the default first track. Ships an optional Vue 3 binding (@vanduo-oss/vd3-cbun/music-player) used here.
Minimal Player
Default config — play/pause, prev/next, volume, track name.
<VdMusicPlayer :tracks="tracks" />With Progress Bar
Enable showProgress for a seek bar + elapsed/duration times.
<VdMusicPlayer :tracks="tracks" :options="{ showProgress: true }" />With Playlist + Progress
Enable showPlaylist and showProgress for a fuller player. Shuffle and repeat are always on the control bar.
<VdMusicPlayer
:tracks="tracks"
:options="{ showPlaylist: true, showProgress: true }"
/>Compact
Minimal floating bar, no track name shown.
<VdMusicPlayer :tracks="tracks" class="vd-music-player-compact" />Small (default size)
Standard layout with track name — use vd-music-player-sm or omit size classes.
<VdMusicPlayer :tracks="tracks" class="vd-music-player-sm" />Large
Roomier padding and controls — vd-music-player-lg for featured placement.
<VdMusicPlayer :tracks="tracks" class="vd-music-player-lg" />Inline Layout — All Features
Full-width bar with progress bar, repeat, shuffle, and playlist.
<VdMusicPlayer
:tracks="tracks"
:options="{ showProgress: true, showPlaylist: true, shuffle: false, repeat: 'off' }"
class="vd-music-player-inline"
/>Glass surface
Add glass: true or the vd-music-player-glass class to any player size or layout. Uses the same frosted tokens as vd-glass in the core framework.
<VdMusicPlayer
:tracks="tracks"
:options="{ showProgress: true, glass: true }"
/>Detachable — fixed corners
detachable: true, minimizable: true, draggable: false. Snap to a corner via floatingPosition or detach(el, position).
Detachable — draggable
draggable: true adds a drag handle when floating. Still supports minimize/expand. Free positioning overrides corner presets.
<script setup lang="ts">
import { ref } from 'vue';
import { VdMusicPlayer } from '@vanduo-oss/vd3-cbun/music-player';
const playerRef = ref();
function detachAndMinimize() {
const api = playerRef.value?.player;
const el = playerRef.value?.container();
if (!api || !el) return;
api.detach(el, 'bottom-right');
api.minimize(el);
}
</script>
<template>
<VdMusicPlayer
ref="playerRef"
:tracks="tracks"
:options="{ showProgress: true, showPlaylist: true, detachable: true, draggable: true, minimizable: true }"
/>
</template>Programmatic API
Drive the player through the exposed player instance.
State: —
<script setup lang="ts">
import { ref } from 'vue';
import { VdMusicPlayer } from '@vanduo-oss/vd3-cbun/music-player';
const playerRef = ref();
// The component exposes { player, container() } — drive it imperatively:
function control() {
const api = playerRef.value?.player;
const el = playerRef.value?.container();
if (!api || !el) return;
api.play(el);
api.pause(el);
api.next(el);
api.previous(el);
api.setTrack(el, 0);
api.setVolume(el, 0.75);
api.shuffle(el);
api.repeat(el);
api.setRepeat(el, 'one');
api.detach(el, 'bottom-right');
api.attach(el);
api.minimize(el);
api.expand(el);
const state = api.getState(el);
}
</script>
<template>
<VdMusicPlayer ref="playerRef" :tracks="tracks" />
</template>Live event log
All 10 player emits are captured here in real time.
- No events yet — interact with the player above.
API Reference
Install
pnpm add @vanduo-oss/vd3-cbunUsage
<script setup lang="ts">
import { VdMusicPlayer } from '@vanduo-oss/vd3-cbun/music-player';
const tracks = [
{ name: 'Pale Blue Dot', url: '/music/pale-blue-dot.mp3' },
{ name: 'Maia Nebula', url: '/music/maia-nebula.mp3' ];
</script>
<template>
<VdMusicPlayer
:tracks="tracks"
:options="{ showPlaylist: true, showProgress: true }"
@trackchange="(d) => console.log(d.name)" />
</template>Component API
| Prop / event | Description |
|---|---|
:tracks | Playlist — [{ name, url }]. |
:options | Player options object (see Options below). |
@play / @pause / @ended | Playback lifecycle. |
@trackchange | Active track changed; detail { index, name, url }. |
@volumechange / @repeatchange | Volume or repeat-mode changed. |
@detach / @attach / @minimize / @expand | Floating-mode transitions. |
@ready | Emitted once with the container element. |
expose: player / container() | Imperative API + container accessor. |
Options
| Option | Type | Default | Description |
|---|---|---|---|
tracks | Array | [] | Array of { name, url } objects. name is displayed; url is the audio file path. |
volume | number | 0.5 | Initial playback volume between 0 and 1. |
shuffle | boolean | false | Shuffle the track list on init. Also shows the shuffle toggle button. |
repeat | string | 'off' | 'off', 'one' (restart current track), or 'all' (wrap playlist). Button cycles modes; repeat-one shows a 1 badge. |
showProgress | boolean | false | Show a seek bar with elapsed/total time display. |
showPlaylist | boolean | false | Show a collapsible track list panel. Also shows the shuffle button. |
autoAdvance | boolean | true | Automatically play the next track when the current one ends. Ignored when repeat is 'one' or 'all'. |
glass | boolean | false | Frosted-glass surface (vd-music-player-glass); pairs with theme --vd-glass-* tokens. |
detachable | boolean | false | Show detach/attach controls and allow detach() to float the player. |
floatingPosition | string | 'bottom-right' | After detaching: 'bottom-left', 'bottom-right', 'top-left', or 'top-right'. |
draggable | boolean | false | When detachable is true, show a drag handle and allow pointer-dragging while floating. |
minimizable | boolean | false | Show a control to collapse the player to essential transport + volume. |
startMinimized | boolean | false | On the first detach(), start in the minimized layout. |
persistPosition | boolean | false | Save floating {x, y} to localStorage (via safeStorage when available). |
persistKey | string | '' (from element id) | Storage key suffix for persistPosition; defaults from the element's id attribute. |
Events
| Event | Description |
|---|---|
musicplayer:play | Playback started. |
musicplayer:pause | Playback paused. |
musicplayer:trackchange | Active track changed; detail { index, name, url }. |
musicplayer:volumechange | Volume changed; detail { volume }. |
musicplayer:repeatchange | Repeat mode changed; detail { repeat }. |
musicplayer:ended | Fires when repeat is off and autoAdvance is false at track end. |
musicplayer:detach | Player floated above the page. |
musicplayer:attach | Player docked back into place. |
musicplayer:minimize | Player collapsed to essential transport + volume. |
musicplayer:expand | Player restored from minimized. |
el.addEventListener('musicplayer:play', () => console.log('Playback started'));
el.addEventListener('musicplayer:trackchange', (e) => {
const { index, name, url } = e.detail;
console.log('Track changed to:', index, name, url);
});
el.addEventListener('musicplayer:volumechange', (e) => console.log('Volume:', e.detail.volume));
el.addEventListener('musicplayer:repeatchange', (e) => console.log('Repeat mode:', e.detail.repeat));
el.addEventListener('musicplayer:detach', () => console.log('Player is floating'));
el.addEventListener('musicplayer:attach', () => console.log('Player was docked'));
el.addEventListener('musicplayer:minimize', () => console.log('Minimized'));
el.addEventListener('musicplayer:expand', () => console.log('Expanded'));CSS Variables
:root {
--vd-music-player-accent: var(--vd-color-primary);
--vd-music-player-bg: var(--vd-bg-primary);
--vd-music-player-bg-secondary: var(--vd-bg-secondary);
--vd-music-player-border: var(--vd-border-color);
--vd-music-player-text: var(--vd-text-primary);
--vd-music-player-text-muted: var(--vd-text-muted);
--vd-music-player-track-bg: var(--vd-border-color);
--vd-music-player-track-fill: var(--vd-color-primary);
--vd-music-player-btn-size: 2.125rem;
--vd-music-player-padding-x: 1.3125rem;
--vd-music-player-padding-y: 0.8125rem;
--vd-music-player-glass-bg: var(--vd-glass-bg-light);
--vd-music-player-glass-border: var(--vd-glass-border-light);
--vd-music-player-glass-shadow: var(--vd-glass-shadow);
--vd-music-player-floating-z: 10050;
--vd-music-player-floating-offset: 1rem;
}Accessibility
- The container is a
role="region"labeled Music Player; transport controls sit in arole="group"labeled Playback controls. - Buttons expose
aria-labeltext (Play/Pause, Previous/Next, Volume, Seek, playlist toggle witharia-expanded, shuffle and repeat witharia-pressed). Repeat uses labels Repeat, Repeat one, and Repeat all; repeat-one shows a visible1badge. - The current track name uses
aria-live="polite"andaria-atomic="true"so screen readers announce track changes. - Playlist items set
aria-currenton the active row; decorative icons usearia-hidden="true". - Play/Pause supports Space and Enter on the play button; range inputs are keyboard-focusable with visible
:focus-visibleoutlines. - Floating toolbar controls include descriptive
aria-labelvalues; minimize togglesaria-expanded. - Glass and floating transitions respect
prefers-reduced-transparencyandprefers-reduced-motion.
A curated track set from Stellardrone, from the album Invent the Universe. Source: Bandcamp. Licensed under CC BY 4.0. No changes were made to the bundled audio files.