13 CSS Neumorphism & Soft UI Designs
Vinyl Player
A deep-slate dark-neumorphic music player with a spinning vinyl disc featuring real grooves via repeating-radial-gradient. Includes an 18-bar animated EQ visualizer synced to playback state, arc progress scrubber with glowing gradient thumb, tonearm SVG, amber accent label, and a full transport control set (shuffle, prev, play/pause, next, loop). DM Serif Display for warm sophistication. Best for music apps, podcast platforms, audio dashboards.
The code
<section class="nm-vin" aria-label="Vinyl player widget">
<div class="card">
<span class="glow" aria-hidden="true"></span>
<span class="glow2" aria-hidden="true"></span>
<div class="disc-wrap">
<div class="disc-outer">
<div class="disc" id="nm-vin-disc">
<div class="disc-label" aria-hidden="true">♪</div>
</div>
<div class="tonearm-wrap" aria-hidden="true">
<svg width="70" height="80" viewBox="0 0 70 80">
<line x1="10" y1="10" x2="50" y2="65" stroke="#888" stroke-width="2.5" stroke-linecap="round"/>
<circle cx="10" cy="10" r="6" fill="#444" stroke="#666" stroke-width="1"/>
<circle cx="50" cy="65" r="4" fill="#e0a86c" opacity="0.9"/>
</svg>
</div>
</div>
</div>
<div class="track-info">
<div class="track-title">Midnight Reverie</div>
<div class="track-artist">Aurelia Voss · Late Hours</div>
</div>
<div class="progress-wrap">
<div class="progress-track">
<div class="progress-fill" id="nm-vin-fill">
<span class="progress-thumb" aria-hidden="true"></span>
</div>
</div>
<div class="progress-times">
<span id="nm-vin-elapsed">1:28</span>
<span>3:52</span>
</div>
</div>
<div class="eq-wrap paused" id="nm-vin-eq" aria-hidden="true">
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
</div>
<div class="controls">
<button type="button" class="ctrl-btn sm" aria-label="Shuffle">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/>
<polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Previous">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="19,20 9,12 19,4"/><rect x="5" y="4" width="2" height="16"/>
</svg>
</button>
<button type="button" class="ctrl-btn lg" id="nm-vin-play" aria-label="Play / Pause">
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="6,4 20,12 6,20"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Next">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="5,4 15,12 5,20"/><rect x="17" y="4" width="2" height="16"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Loop">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/>
<polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/>
</svg>
</button>
</div>
<div class="volume-row">
<span class="vol-icon" aria-hidden="true">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
</svg>
</span>
<div class="vol-track"><div class="vol-fill"></div></div>
<span class="vol-icon" aria-hidden="true">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
<path d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
<path d="M19.07 4.93a10 10 0 0 1 0 14.14"/>
</svg>
</span>
</div>
</div>
</section> <section class="nm-vin" aria-label="Vinyl player widget">
<div class="card">
<span class="glow" aria-hidden="true"></span>
<span class="glow2" aria-hidden="true"></span>
<div class="disc-wrap">
<div class="disc-outer">
<div class="disc" id="nm-vin-disc">
<div class="disc-label" aria-hidden="true">♪</div>
</div>
<div class="tonearm-wrap" aria-hidden="true">
<svg width="70" height="80" viewBox="0 0 70 80">
<line x1="10" y1="10" x2="50" y2="65" stroke="#888" stroke-width="2.5" stroke-linecap="round"/>
<circle cx="10" cy="10" r="6" fill="#444" stroke="#666" stroke-width="1"/>
<circle cx="50" cy="65" r="4" fill="#e0a86c" opacity="0.9"/>
</svg>
</div>
</div>
</div>
<div class="track-info">
<div class="track-title">Midnight Reverie</div>
<div class="track-artist">Aurelia Voss · Late Hours</div>
</div>
<div class="progress-wrap">
<div class="progress-track">
<div class="progress-fill" id="nm-vin-fill">
<span class="progress-thumb" aria-hidden="true"></span>
</div>
</div>
<div class="progress-times">
<span id="nm-vin-elapsed">1:28</span>
<span>3:52</span>
</div>
</div>
<div class="eq-wrap paused" id="nm-vin-eq" aria-hidden="true">
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
<span class="eq-bar"></span><span class="eq-bar"></span><span class="eq-bar"></span>
</div>
<div class="controls">
<button type="button" class="ctrl-btn sm" aria-label="Shuffle">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="16 3 21 3 21 8"/><line x1="4" y1="20" x2="21" y2="3"/>
<polyline points="21 16 21 21 16 21"/><line x1="15" y1="15" x2="21" y2="21"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Previous">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="19,20 9,12 19,4"/><rect x="5" y="4" width="2" height="16"/>
</svg>
</button>
<button type="button" class="ctrl-btn lg" id="nm-vin-play" aria-label="Play / Pause">
<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="6,4 20,12 6,20"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Next">
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<polygon points="5,4 15,12 5,20"/><rect x="17" y="4" width="2" height="16"/>
</svg>
</button>
<button type="button" class="ctrl-btn sm" aria-label="Loop">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/>
<polyline points="7 23 3 19 7 15"/><path d="M21 13v2a4 4 0 0 1-4 4H3"/>
</svg>
</button>
</div>
<div class="volume-row">
<span class="vol-icon" aria-hidden="true">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
</svg>
</span>
<div class="vol-track"><div class="vol-fill"></div></div>
<span class="vol-icon" aria-hidden="true">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
<path d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
<path d="M19.07 4.93a10 10 0 0 1 0 14.14"/>
</svg>
</span>
</div>
</div>
</section>/* ─── 02 Vinyl Player — dark-slate dark-neumorphic music widget ───── */
@import url('https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@300;400&display=swap');
.nm-vin {
--nm-vin-bg: #1e2230;
--nm-vin-neu-dark: #141720;
--nm-vin-neu-light: #282e40;
--nm-vin-text-primary: #d0d4e0;
--nm-vin-text-muted: #606880;
--nm-vin-accent: #e0a86c;
--nm-vin-accent2: #7c6cdb;
--nm-vin-inset: #1b1f2d;
position: relative;
width: 100%;
min-height: 720px;
background: var(--nm-vin-bg);
font-family: 'DM Mono', ui-monospace, monospace;
display: flex;
align-items: center;
justify-content: center;
padding: 32px 16px;
overflow: hidden;
box-sizing: border-box;
}
.nm-vin *,
.nm-vin *::before,
.nm-vin *::after { box-sizing: border-box; }
/* The inner card holds the dark neumorphic shadow */
.nm-vin .card {
position: relative;
width: 100%;
max-width: 400px;
background: var(--nm-vin-bg);
border-radius: 36px;
padding: 40px 34px 36px;
box-shadow:
14px 14px 32px var(--nm-vin-neu-dark),
-10px -10px 28px var(--nm-vin-neu-light);
overflow: hidden;
}
/* Glows — contained inside the card */
.nm-vin .glow {
position: absolute;
width: 200px;
height: 200px;
border-radius: 50%;
background: radial-gradient(circle, rgba(124, 108, 219, 0.08), transparent 70%);
top: -60px;
right: -40px;
pointer-events: none;
}
.nm-vin .glow2 {
position: absolute;
width: 160px;
height: 160px;
border-radius: 50%;
background: radial-gradient(circle, rgba(224, 168, 108, 0.07), transparent 70%);
bottom: -40px;
left: -30px;
pointer-events: none;
}
/* Vinyl disc */
.nm-vin .disc-wrap {
display: flex;
justify-content: center;
position: relative;
}
.nm-vin .disc-outer {
width: 200px;
height: 200px;
border-radius: 50%;
background: var(--nm-vin-bg);
box-shadow:
10px 10px 24px var(--nm-vin-neu-dark),
-8px -8px 20px var(--nm-vin-neu-light);
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.nm-vin .disc {
width: 170px;
height: 170px;
border-radius: 50%;
background:
repeating-radial-gradient(circle at 50% 50%,
transparent 0px, transparent 3px,
rgba(255, 255, 255, 0.02) 3px, rgba(255, 255, 255, 0.02) 4px
),
conic-gradient(from 0deg,
#1a1d2a 0deg, #242838 90deg, #1a1d2a 180deg, #242838 270deg, #1a1d2a 360deg
);
box-shadow:
inset 4px 4px 12px rgba(0, 0, 0, 0.6),
inset -3px -3px 8px rgba(255, 255, 255, 0.04);
display: flex;
align-items: center;
justify-content: center;
animation: nm-vin-spin 4s linear infinite;
animation-play-state: paused;
}
.nm-vin .disc.playing { animation-play-state: running; }
@keyframes nm-vin-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.nm-vin .disc-label {
width: 62px;
height: 62px;
border-radius: 50%;
background: linear-gradient(135deg, var(--nm-vin-accent), #c4863a);
box-shadow:
inset 2px 2px 6px rgba(0, 0, 0, 0.3),
inset -1px -1px 3px rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
color: #1a1d2a;
}
/* Tonearm */
.nm-vin .tonearm-wrap {
position: absolute;
top: -8px;
right: -8px;
width: 70px;
height: 80px;
}
.nm-vin .tonearm-wrap svg {
filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.6));
}
/* Track info */
.nm-vin .track-info { text-align: center; margin-bottom: 22px; }
.nm-vin .track-title {
font-family: 'DM Serif Display', Georgia, serif;
font-size: 22px;
color: var(--nm-vin-text-primary);
margin-bottom: 5px;
letter-spacing: -0.3px;
}
.nm-vin .track-artist {
font-size: 10px;
font-weight: 300;
letter-spacing: 3px;
text-transform: uppercase;
color: var(--nm-vin-text-muted);
}
/* Progress bar */
.nm-vin .progress-wrap { margin-bottom: 22px; position: relative; }
.nm-vin .progress-track {
height: 4px;
border-radius: 2px;
background: var(--nm-vin-inset);
box-shadow:
inset 2px 2px 4px var(--nm-vin-neu-dark),
inset -1px -1px 3px rgba(255, 255, 255, 0.04);
position: relative;
cursor: pointer;
}
.nm-vin .progress-fill {
height: 100%;
width: 38%;
border-radius: 2px;
background: linear-gradient(90deg, var(--nm-vin-accent2), var(--nm-vin-accent));
position: relative;
}
.nm-vin .progress-thumb {
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--nm-vin-bg);
box-shadow:
2px 2px 6px var(--nm-vin-neu-dark),
-1px -1px 4px var(--nm-vin-neu-light);
position: absolute;
right: -6px;
top: -4px;
}
.nm-vin .progress-times {
display: flex;
justify-content: space-between;
margin-top: 8px;
}
.nm-vin .progress-times span {
font-size: 9px;
color: var(--nm-vin-text-muted);
letter-spacing: 1px;
}
/* EQ bars */
.nm-vin .eq-wrap {
display: flex;
align-items: flex-end;
justify-content: center;
gap: 4px;
height: 30px;
margin-bottom: 24px;
}
.nm-vin .eq-bar {
display: block;
width: 4px;
border-radius: 2px;
background: linear-gradient(to top, var(--nm-vin-accent2), var(--nm-vin-accent));
animation: nm-vin-eq 0.6s ease-in-out infinite alternate;
opacity: 0.7;
}
.nm-vin .eq-bar:nth-child(1) { height: 40%; animation-duration: 0.5s; animation-delay: 0.0s; }
.nm-vin .eq-bar:nth-child(2) { height: 70%; animation-duration: 0.7s; animation-delay: 0.1s; }
.nm-vin .eq-bar:nth-child(3) { height: 90%; animation-duration: 0.4s; animation-delay: 0.2s; }
.nm-vin .eq-bar:nth-child(4) { height: 60%; animation-duration: 0.6s; animation-delay: 0.05s; }
.nm-vin .eq-bar:nth-child(5) { height: 100%; animation-duration: 0.8s; animation-delay: 0.15s; }
.nm-vin .eq-bar:nth-child(6) { height: 75%; animation-duration: 0.5s; animation-delay: 0.25s; }
.nm-vin .eq-bar:nth-child(7) { height: 50%; animation-duration: 0.7s; animation-delay: 0.1s; }
.nm-vin .eq-bar:nth-child(8) { height: 85%; animation-duration: 0.4s; animation-delay: 0.3s; }
.nm-vin .eq-bar:nth-child(9) { height: 60%; animation-duration: 0.6s; animation-delay: 0.0s; }
.nm-vin .eq-bar:nth-child(10) { height: 35%; animation-duration: 0.5s; animation-delay: 0.2s; }
.nm-vin .eq-bar:nth-child(11) { height: 80%; animation-duration: 0.9s; animation-delay: 0.05s; }
.nm-vin .eq-bar:nth-child(12) { height: 55%; animation-duration: 0.6s; animation-delay: 0.15s; }
.nm-vin .eq-bar:nth-child(13) { height: 45%; animation-duration: 0.4s; animation-delay: 0.25s; }
.nm-vin .eq-bar:nth-child(14) { height: 70%; animation-duration: 0.7s; animation-delay: 0.1s; }
.nm-vin .eq-bar:nth-child(15) { height: 90%; animation-duration: 0.5s; animation-delay: 0.35s; }
.nm-vin .eq-bar:nth-child(16) { height: 65%; animation-duration: 0.8s; animation-delay: 0.0s; }
.nm-vin .eq-bar:nth-child(17) { height: 40%; animation-duration: 0.6s; animation-delay: 0.2s; }
.nm-vin .eq-bar:nth-child(18) { height: 55%; animation-duration: 0.4s; animation-delay: 0.1s; }
@keyframes nm-vin-eq {
0% { transform: scaleY(0.3); }
100% { transform: scaleY(1); }
}
.nm-vin .eq-wrap.paused .eq-bar { animation-play-state: paused; }
/* Controls */
.nm-vin .controls {
display: flex;
align-items: center;
justify-content: center;
gap: 18px;
}
.nm-vin .ctrl-btn {
border: none;
background: var(--nm-vin-bg);
border-radius: 50%;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--nm-vin-text-muted);
transition: all 0.15s;
}
.nm-vin .ctrl-btn.sm {
width: 40px;
height: 40px;
box-shadow:
4px 4px 10px var(--nm-vin-neu-dark),
-3px -3px 8px var(--nm-vin-neu-light);
font-size: 12px;
}
.nm-vin .ctrl-btn.sm:active {
box-shadow:
inset 2px 2px 6px var(--nm-vin-neu-dark),
inset -1px -1px 4px var(--nm-vin-neu-light);
color: var(--nm-vin-accent);
}
.nm-vin .ctrl-btn.lg {
width: 62px;
height: 62px;
box-shadow:
7px 7px 16px var(--nm-vin-neu-dark),
-6px -6px 14px var(--nm-vin-neu-light);
color: var(--nm-vin-accent);
font-size: 20px;
}
.nm-vin .ctrl-btn.lg:active {
box-shadow:
inset 3px 3px 8px var(--nm-vin-neu-dark),
inset -2px -2px 6px var(--nm-vin-neu-light);
}
/* Volume */
.nm-vin .volume-row {
display: flex;
align-items: center;
gap: 12px;
}
.nm-vin .vol-icon { color: var(--nm-vin-text-muted); font-size: 11px; }
.nm-vin .vol-track {
flex: 1;
height: 4px;
border-radius: 2px;
background: var(--nm-vin-inset);
box-shadow:
inset 2px 2px 4px var(--nm-vin-neu-dark),
inset -1px -1px 3px rgba(255, 255, 255, 0.04);
position: relative;
}
.nm-vin .vol-fill {
height: 100%;
width: 62%;
border-radius: 2px;
background: linear-gradient(90deg, var(--nm-vin-accent2), var(--nm-vin-accent));
}
@media (prefers-reduced-motion: reduce) {
.nm-vin .disc,
.nm-vin .eq-bar { animation: none; }
} /* ─── 02 Vinyl Player — dark-slate dark-neumorphic music widget ───── */
@import url('https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=DM+Mono:wght@300;400&display=swap');
.nm-vin {
--nm-vin-bg: #1e2230;
--nm-vin-neu-dark: #141720;
--nm-vin-neu-light: #282e40;
--nm-vin-text-primary: #d0d4e0;
--nm-vin-text-muted: #606880;
--nm-vin-accent: #e0a86c;
--nm-vin-accent2: #7c6cdb;
--nm-vin-inset: #1b1f2d;
position: relative;
width: 100%;
min-height: 720px;
background: var(--nm-vin-bg);
font-family: 'DM Mono', ui-monospace, monospace;
display: flex;
align-items: center;
justify-content: center;
padding: 32px 16px;
overflow: hidden;
box-sizing: border-box;
}
.nm-vin *,
.nm-vin *::before,
.nm-vin *::after { box-sizing: border-box; }
/* The inner card holds the dark neumorphic shadow */
.nm-vin .card {
position: relative;
width: 100%;
max-width: 400px;
background: var(--nm-vin-bg);
border-radius: 36px;
padding: 40px 34px 36px;
box-shadow:
14px 14px 32px var(--nm-vin-neu-dark),
-10px -10px 28px var(--nm-vin-neu-light);
overflow: hidden;
}
/* Glows — contained inside the card */
.nm-vin .glow {
position: absolute;
width: 200px;
height: 200px;
border-radius: 50%;
background: radial-gradient(circle, rgba(124, 108, 219, 0.08), transparent 70%);
top: -60px;
right: -40px;
pointer-events: none;
}
.nm-vin .glow2 {
position: absolute;
width: 160px;
height: 160px;
border-radius: 50%;
background: radial-gradient(circle, rgba(224, 168, 108, 0.07), transparent 70%);
bottom: -40px;
left: -30px;
pointer-events: none;
}
/* Vinyl disc */
.nm-vin .disc-wrap {
display: flex;
justify-content: center;
position: relative;
}
.nm-vin .disc-outer {
width: 200px;
height: 200px;
border-radius: 50%;
background: var(--nm-vin-bg);
box-shadow:
10px 10px 24px var(--nm-vin-neu-dark),
-8px -8px 20px var(--nm-vin-neu-light);
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.nm-vin .disc {
width: 170px;
height: 170px;
border-radius: 50%;
background:
repeating-radial-gradient(circle at 50% 50%,
transparent 0px, transparent 3px,
rgba(255, 255, 255, 0.02) 3px, rgba(255, 255, 255, 0.02) 4px
),
conic-gradient(from 0deg,
#1a1d2a 0deg, #242838 90deg, #1a1d2a 180deg, #242838 270deg, #1a1d2a 360deg
);
box-shadow:
inset 4px 4px 12px rgba(0, 0, 0, 0.6),
inset -3px -3px 8px rgba(255, 255, 255, 0.04);
display: flex;
align-items: center;
justify-content: center;
animation: nm-vin-spin 4s linear infinite;
animation-play-state: paused;
}
.nm-vin .disc.playing { animation-play-state: running; }
@keyframes nm-vin-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.nm-vin .disc-label {
width: 62px;
height: 62px;
border-radius: 50%;
background: linear-gradient(135deg, var(--nm-vin-accent), #c4863a);
box-shadow:
inset 2px 2px 6px rgba(0, 0, 0, 0.3),
inset -1px -1px 3px rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
color: #1a1d2a;
}
/* Tonearm */
.nm-vin .tonearm-wrap {
position: absolute;
top: -8px;
right: -8px;
width: 70px;
height: 80px;
}
.nm-vin .tonearm-wrap svg {
filter: drop-shadow(2px 2px 4px rgba(0, 0, 0, 0.6));
}
/* Track info */
.nm-vin .track-info { text-align: center; margin-bottom: 22px; }
.nm-vin .track-title {
font-family: 'DM Serif Display', Georgia, serif;
font-size: 22px;
color: var(--nm-vin-text-primary);
margin-bottom: 5px;
letter-spacing: -0.3px;
}
.nm-vin .track-artist {
font-size: 10px;
font-weight: 300;
letter-spacing: 3px;
text-transform: uppercase;
color: var(--nm-vin-text-muted);
}
/* Progress bar */
.nm-vin .progress-wrap { margin-bottom: 22px; position: relative; }
.nm-vin .progress-track {
height: 4px;
border-radius: 2px;
background: var(--nm-vin-inset);
box-shadow:
inset 2px 2px 4px var(--nm-vin-neu-dark),
inset -1px -1px 3px rgba(255, 255, 255, 0.04);
position: relative;
cursor: pointer;
}
.nm-vin .progress-fill {
height: 100%;
width: 38%;
border-radius: 2px;
background: linear-gradient(90deg, var(--nm-vin-accent2), var(--nm-vin-accent));
position: relative;
}
.nm-vin .progress-thumb {
width: 12px;
height: 12px;
border-radius: 50%;
background: var(--nm-vin-bg);
box-shadow:
2px 2px 6px var(--nm-vin-neu-dark),
-1px -1px 4px var(--nm-vin-neu-light);
position: absolute;
right: -6px;
top: -4px;
}
.nm-vin .progress-times {
display: flex;
justify-content: space-between;
margin-top: 8px;
}
.nm-vin .progress-times span {
font-size: 9px;
color: var(--nm-vin-text-muted);
letter-spacing: 1px;
}
/* EQ bars */
.nm-vin .eq-wrap {
display: flex;
align-items: flex-end;
justify-content: center;
gap: 4px;
height: 30px;
margin-bottom: 24px;
}
.nm-vin .eq-bar {
display: block;
width: 4px;
border-radius: 2px;
background: linear-gradient(to top, var(--nm-vin-accent2), var(--nm-vin-accent));
animation: nm-vin-eq 0.6s ease-in-out infinite alternate;
opacity: 0.7;
}
.nm-vin .eq-bar:nth-child(1) { height: 40%; animation-duration: 0.5s; animation-delay: 0.0s; }
.nm-vin .eq-bar:nth-child(2) { height: 70%; animation-duration: 0.7s; animation-delay: 0.1s; }
.nm-vin .eq-bar:nth-child(3) { height: 90%; animation-duration: 0.4s; animation-delay: 0.2s; }
.nm-vin .eq-bar:nth-child(4) { height: 60%; animation-duration: 0.6s; animation-delay: 0.05s; }
.nm-vin .eq-bar:nth-child(5) { height: 100%; animation-duration: 0.8s; animation-delay: 0.15s; }
.nm-vin .eq-bar:nth-child(6) { height: 75%; animation-duration: 0.5s; animation-delay: 0.25s; }
.nm-vin .eq-bar:nth-child(7) { height: 50%; animation-duration: 0.7s; animation-delay: 0.1s; }
.nm-vin .eq-bar:nth-child(8) { height: 85%; animation-duration: 0.4s; animation-delay: 0.3s; }
.nm-vin .eq-bar:nth-child(9) { height: 60%; animation-duration: 0.6s; animation-delay: 0.0s; }
.nm-vin .eq-bar:nth-child(10) { height: 35%; animation-duration: 0.5s; animation-delay: 0.2s; }
.nm-vin .eq-bar:nth-child(11) { height: 80%; animation-duration: 0.9s; animation-delay: 0.05s; }
.nm-vin .eq-bar:nth-child(12) { height: 55%; animation-duration: 0.6s; animation-delay: 0.15s; }
.nm-vin .eq-bar:nth-child(13) { height: 45%; animation-duration: 0.4s; animation-delay: 0.25s; }
.nm-vin .eq-bar:nth-child(14) { height: 70%; animation-duration: 0.7s; animation-delay: 0.1s; }
.nm-vin .eq-bar:nth-child(15) { height: 90%; animation-duration: 0.5s; animation-delay: 0.35s; }
.nm-vin .eq-bar:nth-child(16) { height: 65%; animation-duration: 0.8s; animation-delay: 0.0s; }
.nm-vin .eq-bar:nth-child(17) { height: 40%; animation-duration: 0.6s; animation-delay: 0.2s; }
.nm-vin .eq-bar:nth-child(18) { height: 55%; animation-duration: 0.4s; animation-delay: 0.1s; }
@keyframes nm-vin-eq {
0% { transform: scaleY(0.3); }
100% { transform: scaleY(1); }
}
.nm-vin .eq-wrap.paused .eq-bar { animation-play-state: paused; }
/* Controls */
.nm-vin .controls {
display: flex;
align-items: center;
justify-content: center;
gap: 18px;
}
.nm-vin .ctrl-btn {
border: none;
background: var(--nm-vin-bg);
border-radius: 50%;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--nm-vin-text-muted);
transition: all 0.15s;
}
.nm-vin .ctrl-btn.sm {
width: 40px;
height: 40px;
box-shadow:
4px 4px 10px var(--nm-vin-neu-dark),
-3px -3px 8px var(--nm-vin-neu-light);
font-size: 12px;
}
.nm-vin .ctrl-btn.sm:active {
box-shadow:
inset 2px 2px 6px var(--nm-vin-neu-dark),
inset -1px -1px 4px var(--nm-vin-neu-light);
color: var(--nm-vin-accent);
}
.nm-vin .ctrl-btn.lg {
width: 62px;
height: 62px;
box-shadow:
7px 7px 16px var(--nm-vin-neu-dark),
-6px -6px 14px var(--nm-vin-neu-light);
color: var(--nm-vin-accent);
font-size: 20px;
}
.nm-vin .ctrl-btn.lg:active {
box-shadow:
inset 3px 3px 8px var(--nm-vin-neu-dark),
inset -2px -2px 6px var(--nm-vin-neu-light);
}
/* Volume */
.nm-vin .volume-row {
display: flex;
align-items: center;
gap: 12px;
}
.nm-vin .vol-icon { color: var(--nm-vin-text-muted); font-size: 11px; }
.nm-vin .vol-track {
flex: 1;
height: 4px;
border-radius: 2px;
background: var(--nm-vin-inset);
box-shadow:
inset 2px 2px 4px var(--nm-vin-neu-dark),
inset -1px -1px 3px rgba(255, 255, 255, 0.04);
position: relative;
}
.nm-vin .vol-fill {
height: 100%;
width: 62%;
border-radius: 2px;
background: linear-gradient(90deg, var(--nm-vin-accent2), var(--nm-vin-accent));
}
@media (prefers-reduced-motion: reduce) {
.nm-vin .disc,
.nm-vin .eq-bar { animation: none; }
}(() => {
const root = document.querySelector('.nm-vin');
if (!root) return;
let playing = false;
const disc = root.querySelector('#nm-vin-disc');
const eqWrap = root.querySelector('#nm-vin-eq');
const playBtn = root.querySelector('#nm-vin-play');
const pFill = root.querySelector('#nm-vin-fill');
const elapsedEl = root.querySelector('#nm-vin-elapsed');
let elapsed = 88;
const total = 232;
let interval = null;
const fmt = (s) => Math.floor(s / 60) + ':' + String(s % 60).padStart(2, '0');
const playIcon = '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><polygon points="6,4 20,12 6,20"/></svg>';
const pauseIcon = '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>';
if (playBtn) playBtn.addEventListener('click', () => {
playing = !playing;
if (disc) disc.classList.toggle('playing', playing);
if (playing) {
eqWrap?.classList.remove('paused');
playBtn.innerHTML = pauseIcon;
interval = setInterval(() => {
elapsed = Math.min(elapsed + 1, total);
if (pFill) pFill.style.width = (elapsed / total * 100) + '%';
if (elapsedEl) elapsedEl.textContent = fmt(elapsed);
if (elapsed >= total) { clearInterval(interval); playing = false; }
}, 1000);
} else {
eqWrap?.classList.add('paused');
clearInterval(interval);
playBtn.innerHTML = playIcon;
}
});
})(); (() => {
const root = document.querySelector('.nm-vin');
if (!root) return;
let playing = false;
const disc = root.querySelector('#nm-vin-disc');
const eqWrap = root.querySelector('#nm-vin-eq');
const playBtn = root.querySelector('#nm-vin-play');
const pFill = root.querySelector('#nm-vin-fill');
const elapsedEl = root.querySelector('#nm-vin-elapsed');
let elapsed = 88;
const total = 232;
let interval = null;
const fmt = (s) => Math.floor(s / 60) + ':' + String(s % 60).padStart(2, '0');
const playIcon = '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><polygon points="6,4 20,12 6,20"/></svg>';
const pauseIcon = '<svg width="22" height="22" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/></svg>';
if (playBtn) playBtn.addEventListener('click', () => {
playing = !playing;
if (disc) disc.classList.toggle('playing', playing);
if (playing) {
eqWrap?.classList.remove('paused');
playBtn.innerHTML = pauseIcon;
interval = setInterval(() => {
elapsed = Math.min(elapsed + 1, total);
if (pFill) pFill.style.width = (elapsed / total * 100) + '%';
if (elapsedEl) elapsedEl.textContent = fmt(elapsed);
if (elapsed >= total) { clearInterval(interval); playing = false; }
}, 1000);
} else {
eqWrap?.classList.add('paused');
clearInterval(interval);
playBtn.innerHTML = playIcon;
}
});
})();