Player
A media player with artwork under a magnifying lens and a glass-thumbed scrubber.
Open standalone previewIX
Meridian Nine
Low Tide Collective
1:36−2:38
Install
$ pnpm dlx shadcn@latest add https://vitrumui.vercel.app/r/glass-player.jsonSource
"use client";
import * as React from "react";
import {
PauseIcon,
PlayIcon,
Repeat2Icon,
ShuffleIcon,
SkipBackIcon,
SkipForwardIcon,
Volume2Icon,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Glass } from "@/components/ui/glass";
import { Lens } from "@/components/ui/lens";
import { Slider } from "@/components/ui/slider";
function formatTime(seconds: number): string {
const m = Math.floor(seconds / 60);
const s = Math.floor(seconds % 60);
return `${m}:${String(s).padStart(2, "0")}`;
}
const TRACK_LENGTH = 254;
/**
* Media player: artwork under a magnifying lens, a glass-thumbed
* scrubber, and a pill transport.
*/
export function GlassPlayer() {
const [playing, setPlaying] = React.useState(true);
const [position, setPosition] = React.useState(96);
return (
<Glass
material="slab"
className="w-full max-w-sm rounded-sheet p-6 shadow-glass-lg"
>
<div className="flex flex-col gap-5">
<div className="relative aspect-square w-full overflow-clip rounded-pane bg-[radial-gradient(80%_80%_at_25%_20%,var(--aurora-1),transparent_60%),radial-gradient(70%_70%_at_80%_35%,var(--aurora-2),transparent_65%),radial-gradient(90%_80%_at_50%_100%,var(--aurora-3),transparent_70%)]">
<div className="absolute inset-0 grid place-items-center">
<p className="font-display text-5xl font-bold tracking-tight text-background/80 select-none">
IX
</p>
</div>
<Lens size={120} />
</div>
<div className="flex flex-col gap-0.5">
<p className="font-display text-base font-semibold tracking-tight">
Meridian Nine
</p>
<p className="text-sm text-muted-foreground">Low Tide Collective</p>
</div>
<div className="flex flex-col gap-1.5">
<Slider
value={[position]}
max={TRACK_LENGTH}
onValueChange={([next]) => setPosition(next)}
aria-label="Seek"
/>
<div className="flex justify-between font-mono text-[0.6875rem] text-muted-foreground">
<span>{formatTime(position)}</span>
<span>−{formatTime(TRACK_LENGTH - position)}</span>
</div>
</div>
<div className="flex items-center justify-center gap-2">
<Button variant="ghost" size="icon-sm" aria-label="Shuffle">
<ShuffleIcon />
</Button>
<Button variant="ghost" size="icon" aria-label="Previous track">
<SkipBackIcon />
</Button>
<Button
variant="accent"
size="icon-lg"
aria-label={playing ? "Pause" : "Play"}
aria-pressed={playing}
onClick={() => setPlaying((value) => !value)}
>
{playing ? <PauseIcon /> : <PlayIcon />}
</Button>
<Button variant="ghost" size="icon" aria-label="Next track">
<SkipForwardIcon />
</Button>
<Button variant="ghost" size="icon-sm" aria-label="Repeat">
<Repeat2Icon />
</Button>
</div>
<div className="flex items-center gap-3">
<Volume2Icon className="size-4 shrink-0 text-muted-foreground" />
<Slider defaultValue={[65]} aria-label="Volume" />
</div>
</div>
</Glass>
);
}