feat(ui): rebuild player view to match Studio Reference mockup
Restructures the player tab DOM to actually look like the editorial mockup, not just inherit new fonts. The previous commit only swapped tokens & typography on the legacy Spotify-clone layout. DOM additions (all preserve existing JS-touched IDs): - Vinyl stage: rotating vinyl wrapping the existing #album-art as a circular center label; spins only when state=playing via CSS hook - SVG tonearm: pivots in/out based on data-playstate - Kicker line: copper italic mono header above the track title - Editorial 4-cell metadata grid: State / Source / Elapsed / Length - Decorative spectrum bars (30, CSS-only animation, paused when idle) - VU meter cluster: needle visual driven by volume %, alongside the preserved volume slider for a11y - Folio marks: top-left and top-right of the player container JS hooks (small, additive): - updatePlaybackState now sets :root[data-playstate] for CSS - progress tick mirrors timecode into meta-grid cells - volume update rotates the VU needle - folio-version mirrors the version label i18n: - new keys: player.kicker, player.modes, player.folio_*, meta.* - added to both en.json and ru.json Restored: media_server/static/redesign-mockup.html (Studio Reference visual reference; deleting it in the prior commit was a mistake).
This commit is contained in:
@@ -4292,7 +4292,7 @@ header > div:first-child {
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════
|
||||
PLAYER VIEW — magazine spread
|
||||
PLAYER VIEW — magazine spread with vinyl stage
|
||||
═══════════════════════════════════════════════════════════════ */
|
||||
.player-container {
|
||||
background: transparent;
|
||||
@@ -4300,71 +4300,226 @@ header > div:first-child {
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
margin-top: 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.player-layout {
|
||||
/* Folio marks at top corners of the player container */
|
||||
.player-container > .folio {
|
||||
position: absolute;
|
||||
top: -42px;
|
||||
font-family: var(--mono);
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink-faint);
|
||||
z-index: 1;
|
||||
}
|
||||
.player-container > .folio.tl { left: 0; }
|
||||
.player-container > .folio.tr { right: 0; }
|
||||
|
||||
.player-layout,
|
||||
.now-playing {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(280px, 480px) 1fr;
|
||||
grid-template-columns: minmax(280px, 460px) 1fr;
|
||||
gap: 64px;
|
||||
align-items: start;
|
||||
margin-top: 28px;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.player-layout {
|
||||
.player-layout,
|
||||
.now-playing {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
.album-art-container {
|
||||
/* ─── Vinyl stage ──────────────────────────────────────────── */
|
||||
.album-art-container.vinyl-stage {
|
||||
position: relative;
|
||||
aspect-ratio: 1;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
padding: 14px;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--rule-strong);
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.album-art-container.vinyl-stage:hover { transform: none; }
|
||||
|
||||
.vinyl-stage .vinyl {
|
||||
position: relative;
|
||||
width: 86%;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 50%;
|
||||
background:
|
||||
radial-gradient(circle at 50% 50%,
|
||||
#0a0907 0%,
|
||||
#0a0907 18%,
|
||||
#1a1611 18.3%,
|
||||
#0a0907 18.6%,
|
||||
#14110c 22%,
|
||||
#0a0907 22.3%,
|
||||
#14110c 26%,
|
||||
#0a0907 26.3%,
|
||||
#14110c 30%,
|
||||
#0a0907 30.3%,
|
||||
#14110c 34%,
|
||||
#0a0907 34.3%,
|
||||
#14110c 38%,
|
||||
#0a0907 38.3%,
|
||||
#14110c 42%,
|
||||
#0a0907 42.3%,
|
||||
#14110c 46%,
|
||||
#0a0907 46.3%,
|
||||
#1c1812 47%,
|
||||
#0a0907 100%);
|
||||
box-shadow:
|
||||
0 1px 0 var(--bg-paper),
|
||||
0 28px 60px -20px rgba(0, 0, 0, 0.5),
|
||||
0 8px 20px -8px rgba(0, 0, 0, 0.35);
|
||||
transition: transform 400ms var(--ease);
|
||||
inset 0 0 60px rgba(0,0,0,0.7),
|
||||
0 30px 80px rgba(0,0,0,0.6),
|
||||
0 6px 20px rgba(0,0,0,0.5);
|
||||
animation: sr-vinyl-spin 14s linear infinite;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
.album-art-container:hover { transform: translateY(-2px); }
|
||||
|
||||
#album-art-glow {
|
||||
border-radius: 0;
|
||||
filter: blur(40px) saturate(1.4);
|
||||
opacity: 0.5;
|
||||
inset: 14px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
:root[data-playstate="playing"] .vinyl-stage .vinyl {
|
||||
animation-play-state: running;
|
||||
}
|
||||
|
||||
#album-art {
|
||||
border-radius: 0;
|
||||
.vinyl-stage .vinyl::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 12%;
|
||||
border-radius: 50%;
|
||||
background:
|
||||
conic-gradient(from 0deg,
|
||||
rgba(255,255,255,0.04) 0deg,
|
||||
transparent 30deg,
|
||||
rgba(255,255,255,0.06) 90deg,
|
||||
transparent 150deg,
|
||||
rgba(255,255,255,0.03) 210deg,
|
||||
transparent 270deg,
|
||||
rgba(255,255,255,0.05) 330deg,
|
||||
transparent 360deg);
|
||||
mix-blend-mode: screen;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.vinyl-stage .vinyl-label {
|
||||
position: absolute;
|
||||
inset: 28%;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
box-shadow:
|
||||
inset 0 0 24px rgba(0,0,0,0.4),
|
||||
0 0 0 4px var(--bg-deep),
|
||||
0 0 0 5px var(--copper-lo);
|
||||
background: var(--bg-card);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.vinyl-stage .vinyl-label::after {
|
||||
/* Spindle hole */
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 8%; height: 8%;
|
||||
top: 46%; left: 46%;
|
||||
border-radius: 50%;
|
||||
background: var(--bg-deep);
|
||||
box-shadow: inset 0 1px 2px rgba(255,255,255,0.1);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.vinyl-stage #album-art-glow {
|
||||
position: absolute;
|
||||
inset: -8%;
|
||||
width: 116%;
|
||||
height: 116%;
|
||||
border-radius: 50%;
|
||||
filter: blur(20px) saturate(1.4);
|
||||
opacity: 0.55;
|
||||
z-index: 0;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.vinyl-stage #album-art {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
border-radius: 50%;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.spectrogram-canvas {
|
||||
bottom: 14px;
|
||||
left: 14px;
|
||||
right: 14px;
|
||||
width: auto;
|
||||
/* ─── Tonearm SVG ──────────────────────────────────────────── */
|
||||
.vinyl-stage .tonearm {
|
||||
position: absolute;
|
||||
top: -6%;
|
||||
right: -4%;
|
||||
width: 56%;
|
||||
height: 56%;
|
||||
pointer-events: none;
|
||||
transform-origin: 88% 12%;
|
||||
transform: rotate(-22deg);
|
||||
transition: transform 1s var(--ease);
|
||||
z-index: 3;
|
||||
filter: drop-shadow(0 4px 12px rgba(0,0,0,0.5));
|
||||
}
|
||||
|
||||
:root[data-playstate="playing"] .vinyl-stage .tonearm {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
@keyframes sr-vinyl-spin { to { transform: rotate(360deg); } }
|
||||
|
||||
.vinyl-stage .spectrogram-canvas {
|
||||
position: absolute;
|
||||
bottom: -52px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
border-radius: 0;
|
||||
height: 56px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* ─── Player details (right column / masthead) ──────────────── */
|
||||
.player-details {
|
||||
.player-details,
|
||||
.track-masthead {
|
||||
gap: 0;
|
||||
padding-top: 12px;
|
||||
padding-top: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Kicker — copper italic mono with hairlines */
|
||||
.track-masthead > .kicker {
|
||||
font-family: var(--mono);
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.32em;
|
||||
text-transform: uppercase;
|
||||
color: var(--copper);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
.track-masthead > .kicker::before,
|
||||
.track-masthead > .kicker::after {
|
||||
content: "";
|
||||
height: 1px;
|
||||
background: var(--copper);
|
||||
opacity: 0.6;
|
||||
flex: 0 0 24px;
|
||||
}
|
||||
.track-masthead > .kicker::after { flex: 1 0 auto; }
|
||||
|
||||
.track-info {
|
||||
margin-bottom: 0;
|
||||
position: relative;
|
||||
@@ -4399,27 +4554,225 @@ header > div:first-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.playback-state {
|
||||
margin-top: 22px;
|
||||
padding: 12px 0;
|
||||
/* Editorial 4-cell metadata grid (now houses State / Source / Elapsed / Length) */
|
||||
.meta-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
margin-top: 32px;
|
||||
border-top: 1px solid var(--rule);
|
||||
border-bottom: 1px solid var(--rule);
|
||||
}
|
||||
.meta-cell {
|
||||
padding: 16px 18px 16px 0;
|
||||
border-right: 1px solid var(--rule);
|
||||
min-width: 0;
|
||||
}
|
||||
.meta-cell:last-child { border-right: 0; padding-right: 0; }
|
||||
.meta-cell .meta-label {
|
||||
font-family: var(--mono);
|
||||
font-size: 11px;
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.22em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink-faint);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.meta-cell .meta-value {
|
||||
font-family: var(--serif);
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-size: 18px;
|
||||
color: var(--ink);
|
||||
font-variation-settings: 'opsz' 30;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.meta-cell .meta-value.mono {
|
||||
font-family: var(--mono);
|
||||
font-style: normal;
|
||||
font-size: 15px;
|
||||
color: var(--ink);
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.meta-cell .meta-value .state-icon,
|
||||
.meta-cell .meta-value .source-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
flex-shrink: 0;
|
||||
color: var(--copper);
|
||||
}
|
||||
.meta-cell .source-value { font-size: 16px; }
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.meta-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
.meta-cell:nth-child(2) { border-right: 0; }
|
||||
.meta-cell:nth-child(-n+2) { border-bottom: 1px solid var(--rule); }
|
||||
}
|
||||
|
||||
/* Hide the legacy .playback-state container (its data is now in meta-grid) */
|
||||
.track-info > .playback-state { display: none; }
|
||||
|
||||
/* Spectrum decorative bars */
|
||||
.spectrum {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 3px;
|
||||
height: 38px;
|
||||
margin-top: 28px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.spectrum span {
|
||||
flex: 1;
|
||||
background: linear-gradient(to top, var(--copper-lo), var(--copper-hi));
|
||||
opacity: 0.85;
|
||||
border-radius: 99px 99px 0 0;
|
||||
transform-origin: bottom;
|
||||
animation: sr-spectrum 1.1s ease-in-out infinite;
|
||||
animation-play-state: paused;
|
||||
}
|
||||
:root[data-playstate="playing"] .spectrum span { animation-play-state: running; }
|
||||
|
||||
.spectrum span:nth-child(1) { animation-delay: -0.10s; height: 30%; }
|
||||
.spectrum span:nth-child(2) { animation-delay: -0.45s; height: 60%; }
|
||||
.spectrum span:nth-child(3) { animation-delay: -0.20s; height: 80%; }
|
||||
.spectrum span:nth-child(4) { animation-delay: -0.55s; height: 50%; }
|
||||
.spectrum span:nth-child(5) { animation-delay: -0.30s; height: 95%; }
|
||||
.spectrum span:nth-child(6) { animation-delay: -0.05s; height: 70%; }
|
||||
.spectrum span:nth-child(7) { animation-delay: -0.65s; height: 40%; }
|
||||
.spectrum span:nth-child(8) { animation-delay: -0.25s; height: 85%; }
|
||||
.spectrum span:nth-child(9) { animation-delay: -0.40s; height: 55%; }
|
||||
.spectrum span:nth-child(10) { animation-delay: -0.10s; height: 75%; }
|
||||
.spectrum span:nth-child(11) { animation-delay: -0.50s; height: 35%; }
|
||||
.spectrum span:nth-child(12) { animation-delay: -0.15s; height: 90%; }
|
||||
.spectrum span:nth-child(13) { animation-delay: -0.60s; height: 45%; }
|
||||
.spectrum span:nth-child(14) { animation-delay: -0.30s; height: 65%; }
|
||||
.spectrum span:nth-child(15) { animation-delay: -0.45s; height: 85%; }
|
||||
.spectrum span:nth-child(16) { animation-delay: -0.20s; height: 55%; }
|
||||
.spectrum span:nth-child(17) { animation-delay: -0.55s; height: 70%; }
|
||||
.spectrum span:nth-child(18) { animation-delay: -0.10s; height: 30%; }
|
||||
.spectrum span:nth-child(19) { animation-delay: -0.40s; height: 80%; }
|
||||
.spectrum span:nth-child(20) { animation-delay: -0.25s; height: 60%; }
|
||||
.spectrum span:nth-child(21) { animation-delay: -0.50s; height: 90%; }
|
||||
.spectrum span:nth-child(22) { animation-delay: -0.15s; height: 50%; }
|
||||
.spectrum span:nth-child(23) { animation-delay: -0.60s; height: 70%; }
|
||||
.spectrum span:nth-child(24) { animation-delay: -0.30s; height: 40%; }
|
||||
.spectrum span:nth-child(25) { animation-delay: -0.45s; height: 85%; }
|
||||
.spectrum span:nth-child(26) { animation-delay: -0.20s; height: 55%; }
|
||||
.spectrum span:nth-child(27) { animation-delay: -0.55s; height: 75%; }
|
||||
.spectrum span:nth-child(28) { animation-delay: -0.10s; height: 35%; }
|
||||
.spectrum span:nth-child(29) { animation-delay: -0.40s; height: 65%; }
|
||||
.spectrum span:nth-child(30) { animation-delay: -0.25s; height: 95%; }
|
||||
|
||||
@keyframes sr-spectrum {
|
||||
0%, 100% { transform: scaleY(0.4); }
|
||||
50% { transform: scaleY(1); }
|
||||
}
|
||||
|
||||
/* Transport — wraps progress + controls */
|
||||
.transport {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.progress-row {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
gap: 18px;
|
||||
align-items: center;
|
||||
margin-bottom: 26px;
|
||||
}
|
||||
.progress-row .timecode {
|
||||
font-family: var(--mono);
|
||||
font-size: 12px;
|
||||
color: var(--ink-mute);
|
||||
letter-spacing: 0.06em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.progress-row .timecode.elapsed { color: var(--copper); font-weight: 500; }
|
||||
|
||||
/* Override the legacy .progress-container layout when inside .transport */
|
||||
.transport .progress-container {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* VU cluster (replaces freestanding volume container) */
|
||||
.vu-cluster {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.vu-meter {
|
||||
position: relative;
|
||||
width: 130px;
|
||||
height: 56px;
|
||||
background: linear-gradient(180deg, #1a1610 0%, #0e0c08 100%);
|
||||
border: 1px solid var(--rule-strong);
|
||||
border-radius: 4px 4px 0 0;
|
||||
overflow: hidden;
|
||||
box-shadow: inset 0 2px 6px rgba(0,0,0,0.5), inset 0 0 30px rgba(224,128,56,0.08);
|
||||
}
|
||||
.vu-meter::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: repeating-conic-gradient(from 195deg at 50% 100%,
|
||||
transparent 0deg 4deg,
|
||||
rgba(242, 235, 220, 0.08) 4deg 5deg,
|
||||
transparent 5deg 9deg);
|
||||
}
|
||||
.vu-meter::after {
|
||||
content: "VU";
|
||||
position: absolute;
|
||||
bottom: 4px; left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-family: var(--mono);
|
||||
font-size: 8px;
|
||||
letter-spacing: 0.3em;
|
||||
color: var(--ink-faint);
|
||||
}
|
||||
.vu-needle {
|
||||
position: absolute;
|
||||
bottom: 0; left: 50%;
|
||||
width: 1.5px;
|
||||
height: 88%;
|
||||
background: linear-gradient(to top, var(--copper) 0%, var(--copper-hi) 70%, var(--ink) 100%);
|
||||
transform-origin: bottom center;
|
||||
transform: rotate(-22deg);
|
||||
transition: transform 350ms var(--ease);
|
||||
box-shadow: 0 0 8px var(--copper-glow);
|
||||
}
|
||||
|
||||
/* Volume container nested inside vu-cluster — compact stacked controls */
|
||||
.vu-cluster .volume-container {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
border-top: 0;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
align-items: stretch;
|
||||
min-width: 140px;
|
||||
}
|
||||
.vu-cluster .volume-container > .mute-btn {
|
||||
align-self: flex-start;
|
||||
}
|
||||
.vu-cluster .volume-container > #volume-slider {
|
||||
width: 100%;
|
||||
}
|
||||
.vu-cluster .volume-container > .volume-display {
|
||||
text-align: right;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.16em;
|
||||
text-transform: uppercase;
|
||||
color: var(--ink-mute);
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.state-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: var(--copper);
|
||||
}
|
||||
|
||||
#playback-state {
|
||||
color: var(--ink-soft);
|
||||
.vu-cluster .volume-container > .volume-display::before {
|
||||
content: "VOL · ";
|
||||
color: var(--ink-faint);
|
||||
}
|
||||
|
||||
/* ─── Progress (hairline editorial) ─────────────────────────── */
|
||||
|
||||
Reference in New Issue
Block a user