Componentmedia1.28 KB gzipZero dependencies
Audio Mini
Compact audio player with animated equalizer bars and duration counter.
Preview
Live Preview
CONCERT HALL ACOUSTICS
ORCHESTRA ROW C · SEAT 1201:1204:18
Installation
Tokens guide →Add to your project via CLI (zero dependencies, code you own):
npx bigbullui add audio-miniOr install the full npm package:
npm install bigbulluiUsage
Open in StackBlitzimport { AudioMini } from "@/components/ui/audio-mini";
<AudioMini title="CONCERT LIVE" artist="ORCHESTRA" duration="03:45" />Usage guidance
Use Audio Mini when a ticket stub surface needs Compact audio player with animated equalizer bars and duration counter. Keep props minimal and prefer composition with sibling stubs.
Avoid Audio Mini for long form flows or dense data grids where a dedicated layout block fits better.
Source code
audio-mini.tsxZero dependencies (only React + utils). Copy and paste directly into your project:
"use client";
import * as React from "react";
import { cn } from "./lib/utils";
export interface AudioMiniProps extends React.HTMLAttributes<HTMLDivElement> {
title?: string;
artist?: string;
duration?: string;
src?: string;
className?: string;
}
export function AudioMini({
title = "STAGE LIVE RECORDING",
artist = "ORCHESTRA ROW C",
duration = "03:45",
src,
className,
...props
}: AudioMiniProps) {
const [isPlaying, setIsPlaying] = React.useState(false);
const [progress, setProgress] = React.useState(25);
const audioRef = React.useRef<HTMLAudioElement>(null);
React.useEffect(() => {
let timer: NodeJS.Timeout;
if (isPlaying) {
timer = setInterval(() => {
setProgress((prev) => (prev >= 100 ? 0 : prev + 2));
}, 500);
}
return () => clearInterval(timer);
}, [isPlaying]);
const togglePlay = () => {
if (audioRef.current) {
if (isPlaying) {
audioRef.current.pause();
} else {
audioRef.current.play();
}
}
setIsPlaying(!isPlaying);
};
return (
<div
className={cn(
"inline-flex w-full max-w-sm flex-col gap-2.5 rounded-xl border-2 border-foreground bg-card p-4 shadow-sm outline-1 outline-dashed outline-offset-[-5px] font-mono select-none",
className
)}
{...props}
>
{src && <audio ref={audioRef} src={src} onEnded={() => setIsPlaying(false)} />}
<div className="flex items-center justify-between">
<div className="flex items-center gap-2 truncate">
<button
type="button"
onClick={togglePlay}
aria-label={isPlaying ? "Pause audio" : "Play audio"}
className="flex size-8 shrink-0 items-center justify-center rounded-sm border border-foreground bg-accent text-accent-foreground shadow-xs transition-transform hover:scale-105 active:scale-95 cursor-pointer"
>
<span className="text-xs font-bold">{isPlaying ? "❚❚" : "▶"}</span>
</button>
<div className="truncate">
<h4 className="truncate text-xs font-bold text-foreground uppercase">{title}</h4>
<span className="text-[10px] text-muted-foreground uppercase">{artist}</span>
</div>
</div>
{/* Equalizer Sound Waves */}
<div className="flex items-end gap-1 h-5 ps-2">
{[40, 80, 55, 100, 70].map((h, i) => (
<div
key={i}
style={{ height: isPlaying ? `${h}%` : "20%" }}
className={cn(
"w-1 rounded-xs bg-accent transition-all duration-300",
isPlaying && "animate-pulse"
)}
/>
))}
</div>
</div>
{/* Progress Track */}
<div className="space-y-1">
<div className="relative h-1.5 w-full overflow-hidden rounded-xs bg-secondary border border-border">
<div
style={{ width: `${progress}%` }}
className="h-full bg-foreground transition-all duration-300"
/>
</div>
<div className="flex justify-between text-[9px] text-muted-foreground">
<span>01:12</span>
<span>{duration}</span>
</div>
</div>
</div>
);
}
Props
| Prop | Type | Description |
|---|---|---|
| title? | string | |
| artist? | string | |
| duration? | string | |
| src? | string | |
| className? | string |