Redesign Classic layout for desktop and mobile
This commit is contained in:
@@ -6533,6 +6533,13 @@ const Gestures = (() => {
|
||||
* and reopen. Fold state is per device and rides in settings.
|
||||
* ========================================================================== */
|
||||
const SectionRail = (() => {
|
||||
const CLASSIC_ICONS = {
|
||||
player: '<rect x="3" y="4" width="18" height="13" rx="2"/><path d="M8 21h8m-4-4v4"/>',
|
||||
meta: '<path d="M9 18V5l11-2v13M9 8l11-2"/><ellipse cx="6" cy="18" rx="3" ry="2"/><ellipse cx="17" cy="16" rx="3" ry="2"/>',
|
||||
notes: '<path d="M14 3H4v18h16V11M9 15l2-5 7-7 3 3-7 7z"/>',
|
||||
related: '<path d="m10 14 4-4m-5 6-1 1a4 4 0 0 1-6-6l4-4a4 4 0 0 1 6 0m0 10a4 4 0 0 0 6 0l4-4a4 4 0 0 0-6-6l-1 1"/>',
|
||||
list: '<path d="M9 6h12M9 12h12M9 18h12M3 6h1M3 12h1M3 18h1"/>',
|
||||
};
|
||||
const SECTIONS = [
|
||||
{ id: 'player', sel: '.player-stage', icon: '📺', label: 'Player' },
|
||||
{ id: 'meta', sel: '#nowPlayingMeta', icon: '🎵', label: 'Now playing', fold: true },
|
||||
@@ -6617,7 +6624,7 @@ const SectionRail = (() => {
|
||||
btn.className = 'sec-rail-btn';
|
||||
btn.dataset.sec = sec.id;
|
||||
btn.setAttribute('aria-label', sec.label);
|
||||
btn.innerHTML = `<span aria-hidden="true">${sec.icon}</span><span class="sec-rail-label">${escapeHtml(sec.label)}</span>`;
|
||||
btn.innerHTML = `<span class="legacy-nav-icon" aria-hidden="true">${sec.icon}</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true">${CLASSIC_ICONS[sec.id]}</svg><span class="sec-rail-label">${escapeHtml(sec.label)}</span>`;
|
||||
btn.addEventListener('click', () => onTap(sec, btn));
|
||||
r.appendChild(btn);
|
||||
st.btns.set(sec.id, btn);
|
||||
@@ -7170,7 +7177,11 @@ const Party = (() => {
|
||||
function render() {
|
||||
const panel = $('partyPanel');
|
||||
const chip = $('navPartyBtn');
|
||||
if (chip) chip.textContent = inParty() ? `🎉 Party · ${st.members.length}` : '🎉 Watch party';
|
||||
if (chip) {
|
||||
const label = chip.querySelector('.nav-label');
|
||||
if (label) label.textContent = inParty() ? `Party · ${st.members.length}` : 'Watch party';
|
||||
else chip.textContent = inParty() ? `🎉 Party · ${st.members.length}` : '🎉 Watch party';
|
||||
}
|
||||
if (!panel || panel.classList.contains('hidden')) return;
|
||||
const nameOk = !!myName();
|
||||
if (!inParty()) {
|
||||
@@ -10200,6 +10211,63 @@ function isPortraitPWA() {
|
||||
return _portraitPwaMQ.matches;
|
||||
}
|
||||
|
||||
// Classic uses one scroll container at phone and intermediate widths in both
|
||||
// browser and installed mode. Read CSS so navigation follows the actual layout.
|
||||
function isStackedPlayerLayout() {
|
||||
if (document.documentElement.dataset.layout === 'classic') {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue('--classic-stacked').trim() === '1';
|
||||
}
|
||||
return isPortraitPWA();
|
||||
}
|
||||
|
||||
function playerScrollBehavior() {
|
||||
return data.settings.reduceMotion || window.matchMedia('(prefers-reduced-motion: reduce)').matches ? 'instant' : 'smooth';
|
||||
}
|
||||
|
||||
function setupClassicLayout() {
|
||||
const root = document.documentElement;
|
||||
const search = $('searchInput');
|
||||
const originalPlaceholder = search.getAttribute('placeholder');
|
||||
const bars = [$('bottomNav'), $('miniBar'), $('sectionRail')].filter(Boolean);
|
||||
const heightVars = ['--classic-nav-height', '--classic-mini-height', '--classic-rail-height'];
|
||||
let pending = 0;
|
||||
function measure() {
|
||||
pending = 0;
|
||||
const classic = root.dataset.layout === 'classic';
|
||||
search.placeholder = classic ? 'Search YouTube…' : originalPlaceholder;
|
||||
if (!classic) return;
|
||||
for (const [index, el] of bars.entries()) {
|
||||
const height = el.getClientRects().length ? el.offsetHeight : 0;
|
||||
root.style.setProperty(heightVars[index], height + 'px');
|
||||
}
|
||||
}
|
||||
function schedule() {
|
||||
if (!pending) pending = requestAnimationFrame(measure);
|
||||
}
|
||||
const observer = new MutationObserver(schedule);
|
||||
observer.observe(root, { attributes: true, attributeFilter: ['data-layout', 'data-font', 'data-density'] });
|
||||
bars.forEach(el => observer.observe(el, { attributes: true, attributeFilter: ['class', 'style'] }));
|
||||
if (typeof ResizeObserver !== 'undefined') {
|
||||
const resize = new ResizeObserver(schedule);
|
||||
bars.forEach(el => resize.observe(el));
|
||||
}
|
||||
window.addEventListener('resize', schedule);
|
||||
if (window.visualViewport) window.visualViewport.addEventListener('resize', schedule);
|
||||
// Use the actual clipped control deck, not document scroll position: desktop
|
||||
// panes and the mobile body have different scroll owners.
|
||||
if (typeof IntersectionObserver !== 'undefined') {
|
||||
const mini = $('miniBar');
|
||||
const visibility = new IntersectionObserver(([entry]) => {
|
||||
if (root.dataset.layout !== 'classic' || !current || !current.meta) return;
|
||||
if (entry.isIntersecting) {
|
||||
if (!mini.classList.contains('hidden')) hideMiniBar();
|
||||
} else if (mini.classList.contains('hidden')) showMiniBar();
|
||||
}, { threshold: 0 });
|
||||
visibility.observe($('controls'));
|
||||
}
|
||||
measure();
|
||||
}
|
||||
|
||||
function applyPortraitPwaClass() {
|
||||
const app = document.querySelector('.app');
|
||||
if (!app) return;
|
||||
@@ -10213,10 +10281,10 @@ function applyPortraitPwaClass() {
|
||||
// In portrait mode, .body is the single scroll container (player pane on top,
|
||||
// list pane below). Scrolling it to 0 brings the player fully into view.
|
||||
function scrollPlayerIntoViewPortrait() {
|
||||
if (!isPortraitPWA()) return;
|
||||
if (!isStackedPlayerLayout()) return;
|
||||
const body = document.querySelector('.body');
|
||||
if (body) {
|
||||
body.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
body.scrollTo({ top: 0, behavior: playerScrollBehavior() });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10226,14 +10294,14 @@ function scrollPlayerIntoViewPortrait() {
|
||||
// fresh view sits invisibly below the fold and every nav tap looks dead
|
||||
// (the mobile "can't navigate while playing" bug).
|
||||
function scrollListIntoViewPortrait() {
|
||||
if (!isPortraitPWA()) return;
|
||||
if (!isStackedPlayerLayout()) return;
|
||||
const body = document.querySelector('.body');
|
||||
const listPane = document.querySelector('.list-pane');
|
||||
// offsetParent is null while the pane is hidden (pre-first-search) — its
|
||||
// rect would be all zeros and scroll the body to a bogus offset.
|
||||
if (!body || !listPane || !listPane.offsetParent) return;
|
||||
const top = body.scrollTop + listPane.getBoundingClientRect().top - body.getBoundingClientRect().top;
|
||||
body.scrollTo({ top, behavior: 'smooth' });
|
||||
body.scrollTo({ top, behavior: playerScrollBehavior() });
|
||||
}
|
||||
|
||||
function setupPortraitPwaWatcher() {
|
||||
@@ -10855,7 +10923,7 @@ document.querySelectorAll('.chip').forEach((c) => {
|
||||
// .body pane (the designated scroll container) — scrollIntoView() also
|
||||
// scrolls overflow:hidden ancestors up to <html> on iOS, leaving the
|
||||
// document offset and fixed-element hit testing broken.
|
||||
if (isPortraitPWA()) scrollPlayerIntoViewPortrait();
|
||||
if (isStackedPlayerLayout()) scrollPlayerIntoViewPortrait();
|
||||
else els.playerPane.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
});
|
||||
// Opens the set-list view for whatever is already playing. No playlist arg,
|
||||
@@ -11774,6 +11842,7 @@ async function boot() {
|
||||
wireUI();
|
||||
wireShortcutHelp();
|
||||
setupPortraitPwaWatcher();
|
||||
setupClassicLayout();
|
||||
setupViewportAnchorGuard();
|
||||
try {
|
||||
const loaded = await API.loadData();
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
<link rel="stylesheet" href="service-ui.css" />
|
||||
<link rel="stylesheet" href="refresh.css" />
|
||||
<link rel="stylesheet" href="theme-glass.css" />
|
||||
<link rel="stylesheet" href="theme-classic.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
@@ -35,17 +36,17 @@
|
||||
</div>
|
||||
|
||||
<nav class="nav">
|
||||
<button class="nav-item active" data-view="search">🔍 Search</button>
|
||||
<button class="nav-item" data-view="queue">▶ Queue <span id="navQueueCount" class="nav-badge hidden">0</span></button>
|
||||
<button class="nav-item" data-view="history">🕘 History</button>
|
||||
<button class="nav-item" data-view="saved">💾 Saved</button>
|
||||
<button class="nav-item" data-view="downloads">⬇ Downloads <span id="navDlCount" class="nav-badge hidden">0</span></button>
|
||||
<button class="nav-item" data-view="library">📁 Library</button>
|
||||
<button class="nav-item" data-view="notes">📝 Notes</button>
|
||||
<button class="nav-item" data-view="stats">📊 Stats</button>
|
||||
<button class="nav-item" data-view="settings">⚙ Settings</button>
|
||||
<button class="nav-item" id="navPartyBtn" type="button" title="Watch together with friends">🎉 Watch party</button>
|
||||
<button class="nav-item" id="navRemoteBtn" type="button" title="Control this screen from a phone, or use this phone as a remote">📱 Remote</button>
|
||||
<button class="nav-item active" data-view="search"><span class="legacy-nav-icon" aria-hidden="true">🔍</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><circle cx="10.5" cy="10.5" r="6.5"/><path d="m16 16 5 5"/></svg> Search</button>
|
||||
<button class="nav-item" data-view="queue"><span class="legacy-nav-icon" aria-hidden="true">▶</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="m8 5 11 7-11 7z"/></svg> Queue <span id="navQueueCount" class="nav-badge hidden">0</span></button>
|
||||
<button class="nav-item" data-view="history"><span class="legacy-nav-icon" aria-hidden="true">🕘</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg> History</button>
|
||||
<button class="nav-item" data-view="saved"><span class="legacy-nav-icon" aria-hidden="true">💾</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="M5 3h12l4 4v14H3V3h2zm2 0v6h10V3M7 21v-8h10v8"/></svg> Saved</button>
|
||||
<button class="nav-item" data-view="downloads"><span class="legacy-nav-icon" aria-hidden="true">⬇</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v12m-5-5 5 5 5-5M4 16v5h16v-5"/></svg> Downloads <span id="navDlCount" class="nav-badge hidden">0</span></button>
|
||||
<button class="nav-item" data-view="library"><span class="legacy-nav-icon" aria-hidden="true">📁</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="M3 6h7l2 3h9v11H3z"/></svg> Library</button>
|
||||
<button class="nav-item" data-view="notes"><span class="legacy-nav-icon" aria-hidden="true">📝</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="M14 3H4v18h16V11M9 15l2-5 7-7 3 3-7 7z"/></svg> Notes</button>
|
||||
<button class="nav-item" data-view="stats"><span class="legacy-nav-icon" aria-hidden="true">📊</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="M4 21V11h4v10M10 21V3h4v18M16 21V7h4v14"/></svg> Stats</button>
|
||||
<button class="nav-item" data-view="settings"><span class="legacy-nav-icon" aria-hidden="true">⚙</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="m9 3-1 3-3 1-2 3 2 2-1 3 2 3 3-1 2 3h3l1-3 3-1 2-3-2-2 1-3-2-3-3 1-2-3z"/></svg> Settings</button>
|
||||
<button class="nav-item" id="navPartyBtn" type="button" title="Watch together with friends"><span class="legacy-nav-icon" aria-hidden="true">🎉</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="m4 20 4-13 9 9zM14 3v4m4 1 3-2m-2 5 3 2"/></svg> <span class="nav-label">Watch party</span></button>
|
||||
<button class="nav-item" id="navRemoteBtn" type="button" title="Control this screen from a phone, or use this phone as a remote"><span class="legacy-nav-icon" aria-hidden="true">📱</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><rect x="6" y="2" width="12" height="20" rx="2"/><path d="M10 18h4"/></svg> Remote</button>
|
||||
</nav>
|
||||
|
||||
<div class="pl-header">
|
||||
@@ -96,6 +97,7 @@
|
||||
<form id="searchForm" class="search-form">
|
||||
<input
|
||||
id="searchInput"
|
||||
aria-label="Search YouTube"
|
||||
type="text"
|
||||
placeholder="Search YouTube… (no login, no ads)"
|
||||
autocomplete="off"
|
||||
@@ -180,7 +182,7 @@
|
||||
<!-- Audio-only: loudness waveform drawn behind the transparent range input -->
|
||||
<canvas id="waveCanvas" class="wave-canvas" aria-hidden="true"></canvas>
|
||||
<span id="waveTip" class="wave-tip hidden"></span>
|
||||
<input id="seek" class="seek" type="range" min="0" max="1000" value="0" />
|
||||
<input id="seek" aria-label="Playback position" class="seek" type="range" min="0" max="1000" value="0" />
|
||||
<div id="seekMarkers" class="seek-markers" aria-hidden="true"></div>
|
||||
<div id="abIndicator" class="ab-indicator hidden">
|
||||
<span id="abALabel" class="ab-label">A: --</span>
|
||||
@@ -574,29 +576,29 @@
|
||||
|
||||
<nav id="bottomNav" class="bottom-nav" aria-label="Main navigation">
|
||||
<button class="bottom-nav-btn active" data-view="search" aria-label="Search">
|
||||
<span class="bottom-nav-icon">🔍</span>
|
||||
<span class="bottom-nav-icon"><span class="legacy-nav-icon" aria-hidden="true">🔍</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><circle cx="10.5" cy="10.5" r="6.5"/><path d="m16 16 5 5"/></svg></span>
|
||||
<span class="bottom-nav-label">Search</span>
|
||||
</button>
|
||||
<button class="bottom-nav-btn" data-view="queue" aria-label="Queue">
|
||||
<span class="bottom-nav-icon">▶</span>
|
||||
<span class="bottom-nav-icon"><span class="legacy-nav-icon" aria-hidden="true">▶</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="m8 5 11 7-11 7z"/></svg></span>
|
||||
<span class="bottom-nav-label">Queue</span>
|
||||
<span id="bnQueueCount" class="bottom-nav-badge hidden">0</span>
|
||||
</button>
|
||||
<button class="bottom-nav-btn" data-view="history" aria-label="History">
|
||||
<span class="bottom-nav-icon">🕘</span>
|
||||
<span class="bottom-nav-icon"><span class="legacy-nav-icon" aria-hidden="true">🕘</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></svg></span>
|
||||
<span class="bottom-nav-label">History</span>
|
||||
</button>
|
||||
<button class="bottom-nav-btn" data-view="saved" aria-label="Saved">
|
||||
<span class="bottom-nav-icon">💾</span>
|
||||
<span class="bottom-nav-icon"><span class="legacy-nav-icon" aria-hidden="true">💾</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="M5 3h12l4 4v14H3V3h2zm2 0v6h10V3M7 21v-8h10v8"/></svg></span>
|
||||
<span class="bottom-nav-label">Saved</span>
|
||||
</button>
|
||||
<button class="bottom-nav-btn" data-view="downloads" aria-label="Downloads">
|
||||
<span class="bottom-nav-icon">⬇</span>
|
||||
<span class="bottom-nav-icon"><span class="legacy-nav-icon" aria-hidden="true">⬇</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v12m-5-5 5 5 5-5M4 16v5h16v-5"/></svg></span>
|
||||
<span class="bottom-nav-label">Downloads</span>
|
||||
<span id="bnDlCount" class="bottom-nav-badge hidden">0</span>
|
||||
</button>
|
||||
<button class="bottom-nav-btn" data-view="settings" aria-label="Settings">
|
||||
<span class="bottom-nav-icon">⚙</span>
|
||||
<span class="bottom-nav-icon"><span class="legacy-nav-icon" aria-hidden="true">⚙</span><svg class="classic-nav-icon" hidden viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="m9 3-1 3-3 1-2 3 2 2-1 3 2 3 3-1 2 3h3l1-3 3-1 2-3-2-2 1-3-2-3-3 1-2-3z"/></svg></span>
|
||||
<span class="bottom-nav-label">More</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
@@ -59,6 +59,7 @@ const SHELL = [
|
||||
'/service-ui.css',
|
||||
'/refresh.css',
|
||||
'/theme-glass.css',
|
||||
'/theme-classic.css',
|
||||
'/flag-ui.js',
|
||||
'/export.js',
|
||||
'/async-guard.js',
|
||||
|
||||
228
frontend/theme-classic.css
Normal file
228
frontend/theme-classic.css
Normal file
@@ -0,0 +1,228 @@
|
||||
/* Classic: a media workspace. Keep all overrides inside this layout. */
|
||||
html[data-layout="classic"] {
|
||||
--radius: 12px;
|
||||
--radius-sm: 8px;
|
||||
--classic-stacked: 0;
|
||||
--classic-zoom: 1;
|
||||
--classic-space: 24px;
|
||||
--classic-nav-height: 0px;
|
||||
--classic-mini-height: 0px;
|
||||
--classic-rail-height: 0px;
|
||||
--classic-clearance: calc(var(--classic-nav-height) + var(--classic-mini-height) + var(--classic-rail-height));
|
||||
--accent-glow: transparent;
|
||||
}
|
||||
html[data-layout="classic"][data-font="small"] { --classic-zoom: .9; }
|
||||
html[data-layout="classic"][data-font="large"] { --classic-zoom: 1.12; }
|
||||
html[data-layout="classic"][data-font="xl"] { --classic-zoom: 1.26; }
|
||||
html[data-layout="classic"][data-theme="dark"] {
|
||||
--bg: #121315;
|
||||
--bg-1: #18191c;
|
||||
--bg-2: #202226;
|
||||
--bg-3: #2b2e33;
|
||||
--line: #42464d;
|
||||
--line-soft: #2b2e33;
|
||||
--text: #f3f1ed;
|
||||
--text-2: #c3c4c7;
|
||||
--text-dim: #a1a5ac;
|
||||
--accent: #f06b53;
|
||||
--accent-bright: #ff947e;
|
||||
--accent-deep: #c6442f;
|
||||
}
|
||||
html[data-layout="classic"][data-theme="light"] { --text-dim: #625e59; --accent-deep: #b43623; }
|
||||
html[data-layout="classic"][data-theme] .hero-title { background: none; -webkit-text-fill-color: var(--text); }
|
||||
html[data-layout="classic"] body::before,
|
||||
html[data-layout="classic"] body::after { display: none; }
|
||||
html[data-layout="classic"] ::selection { background: var(--accent); color: var(--bg); }
|
||||
html[data-layout="classic"] :is(input, textarea) { caret-color: var(--accent); }
|
||||
html[data-layout="classic"] :is(button, a, input, select, textarea, [tabindex]):focus-visible {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
html[data-layout="classic"] :is(button, input, select, textarea) { font-family: var(--ui); }
|
||||
html[data-layout="classic"] :is(input, textarea)::placeholder { color: var(--text-dim); opacity: 1; }
|
||||
html[data-layout="classic"] .app { grid-template-columns: 232px minmax(0, 1fr); height: calc(100dvh / var(--classic-zoom)); }
|
||||
html[data-layout="classic"] .main { min-width: 0; min-height: 0; padding-bottom: var(--classic-mini-height); }
|
||||
html[data-layout="classic"] .body { min-height: 0; flex-direction: row; }
|
||||
html[data-layout="classic"] .sidebar {
|
||||
background: var(--bg-1); padding: 28px 12px 20px;
|
||||
border-right: 1px solid var(--line-soft);
|
||||
}
|
||||
html[data-layout="classic"] .brand { font-size: 21px; padding: 0 12px 28px; gap: 10px; }
|
||||
html[data-layout="classic"] .brand-mark { background: var(--accent-deep); box-shadow: none; border-radius: 8px; }
|
||||
html[data-layout="classic"] .nav { gap: 2px; }
|
||||
html[data-layout="classic"] .nav-item { display: flex; align-items: center; gap: 12px; min-height: 44px; padding: 10px 12px; letter-spacing: 0; }
|
||||
html[data-layout="classic"] .nav-item::before { display: none; }
|
||||
html[data-layout="classic"] .nav-item.active { background: var(--bg-3); color: var(--text); }
|
||||
html[data-layout="classic"] .nav-item.active .classic-nav-icon { color: var(--accent); }
|
||||
html[data-layout="classic"] .nav-badge { margin-left: auto; }
|
||||
html[data-layout="classic"] .legacy-nav-icon { display: none; }
|
||||
html[data-layout="classic"] .classic-nav-icon { display: inline-block; flex: none; width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
|
||||
html[data-layout="classic"] .pl-header { margin-top: 16px; font: 600 13px var(--ui); letter-spacing: 0; text-transform: none; }
|
||||
html[data-layout="classic"] .playlist-item { min-height: 44px; border-radius: 8px; }
|
||||
html[data-layout="classic"] .topbar {
|
||||
background: var(--bg); border-bottom: 1px solid var(--line-soft); backdrop-filter: none;
|
||||
padding: 18px var(--classic-space); gap: 12px; flex: none;
|
||||
}
|
||||
html[data-layout="classic"] .search-form { width: 100%; min-width: 0; max-width: 860px; gap: 8px; }
|
||||
html[data-layout="classic"] #searchInput { min-width: 0; font-size: 16px; padding: 11px 14px; min-height: 46px; border-radius: 8px; background: var(--bg-1); box-shadow: none; }
|
||||
html[data-layout="classic"] .search-btn { min-height: 46px; padding: 0 22px; border-radius: 8px; background: var(--bg-3); color: var(--text); box-shadow: none; transform: none; }
|
||||
html[data-layout="classic"] .search-btn:hover { background: var(--accent-deep); color: #fff; }
|
||||
html[data-layout="classic"] .sidebar-toggle { min-width: 44px; min-height: 44px; border-radius: 8px; box-shadow: none; }
|
||||
html[data-layout="classic"] .player-pane {
|
||||
container-type: inline-size;
|
||||
flex: 1 1 0; min-width: 0; min-height: 0; gap: 0;
|
||||
padding: var(--classic-space); padding-bottom: calc(var(--classic-space) + var(--classic-mini-height));
|
||||
overflow-y: auto;
|
||||
}
|
||||
html[data-layout="classic"] .player-stage { border-radius: 12px; box-shadow: none; flex: none; }
|
||||
html[data-layout="classic"] .player-pane:not(.empty) .player-stage { box-shadow: none; }
|
||||
html[data-layout="classic"] .player-pane.empty .player-stage {
|
||||
aspect-ratio: auto; min-height: 0; max-height: none; flex: none; overflow: visible; background: transparent;
|
||||
}
|
||||
html[data-layout="classic"] .player-pane.empty #video { display: none; }
|
||||
html[data-layout="classic"] .player-placeholder {
|
||||
position: relative; inset: auto; width: 100%; padding: 48px 16px; background: transparent; text-align: left;
|
||||
}
|
||||
html[data-layout="classic"] .hero { align-items: flex-start; max-width: 620px; gap: 24px; animation: none; }
|
||||
html[data-layout="classic"] .ph-logo { width: 52px; height: 52px; border-radius: 12px; background: var(--accent-deep); color: #fff; box-shadow: none; border: 0; animation: none; font-size: 20px; }
|
||||
html[data-layout="classic"] .hero-title { font-size: clamp(36px, 4vw, 60px); line-height: 1.05; letter-spacing: -0.035em; background: none; color: var(--text); -webkit-text-fill-color: var(--text); margin: 0; }
|
||||
html[data-layout="classic"] .hero-tagline { font-size: 17px; line-height: 1.65; max-width: 45ch; color: var(--text-2); text-align: left; margin: 0; }
|
||||
html[data-layout="classic"] .hero-pills { justify-content: flex-start; gap: 8px 16px; }
|
||||
html[data-layout="classic"] .hero-pill { padding: 0; background: none; border: 0; font: 500 13px var(--ui); color: var(--text-2); box-shadow: none; }
|
||||
html[data-layout="classic"] .hero-suggests { justify-content: flex-start; gap: 8px; padding-top: 24px; border-top: 1px solid var(--line-soft); width: 100%; }
|
||||
html[data-layout="classic"] .hero-suggests-label { flex-basis: 100%; text-align: left; font: 600 14px var(--ui); text-transform: none; letter-spacing: 0; }
|
||||
html[data-layout="classic"] .chip { min-height: 44px; padding: 10px 14px; background: var(--bg-2); border: 1px solid var(--line-soft); border-radius: 8px; font: 500 14px var(--ui); color: var(--text); transform: none; }
|
||||
html[data-layout="classic"] .player-placeholder.showing-playlists { padding: 8px 0 24px; }
|
||||
html[data-layout="classic"] .home-pl { overflow: visible; max-height: none; animation: none; gap: 24px; }
|
||||
html[data-layout="classic"] .home-pl-title { font-size: 28px; }
|
||||
html[data-layout="classic"] .home-pl-new { min-height: 44px; padding: 8px 14px; }
|
||||
html[data-layout="classic"] .home-pl-grid { overflow: visible; grid-template-columns: repeat(auto-fill, minmax(138px, 1fr)); gap: 24px 16px; max-height: none; mask-image: none; -webkit-mask-image: none; }
|
||||
html[data-layout="classic"] .home-pl-name { font: 600 14px/1.4 var(--ui); }
|
||||
html[data-layout="classic"] #controls { padding: 18px 0; margin-top: 8px; border: 0; border-bottom: 1px solid var(--line-soft); border-radius: 0; background: transparent; box-shadow: none; gap: 14px; }
|
||||
html[data-layout="classic"] #controls .deck-main { grid-template-columns: repeat(5, auto); gap: 14px; }
|
||||
html[data-layout="classic"] #controls .deck-main .skip10 { display: grid; }
|
||||
html[data-layout="classic"] #controls .deck-main .ctrl { width: 48px; height: 48px; border-radius: 12px; }
|
||||
html[data-layout="classic"] #controls .deck-main .ctrl.play { width: 64px; height: 64px; border-radius: 50%; background: var(--accent); color: #171719; box-shadow: none; }
|
||||
html[data-layout="classic"] #controls .deck-main .ctrl.play:hover { background: var(--accent-bright); }
|
||||
html[data-layout="classic"] #controls .ctrl.active svg { filter: none; }
|
||||
html[data-layout="classic"] #controls .time { font: 12px var(--mono); font-variant-numeric: tabular-nums; }
|
||||
html[data-layout="classic"] #controls .deck-vol { padding: 0; gap: 12px; }
|
||||
html[data-layout="classic"] #controls .vol-pod { flex: 1; min-width: 0; display: flex; align-items: center; gap: 12px; }
|
||||
html[data-layout="classic"] #controls .deck-vol .ctrl { width: 44px; height: 44px; }
|
||||
html[data-layout="classic"] #controls .dk-quick { flex-wrap: wrap; gap: 8px; }
|
||||
html[data-layout="classic"] #controls .dk-quick #fsBtn { min-height: 48px; height: auto; border-radius: 8px; background: var(--bg-2); }
|
||||
html[data-layout="classic"] #controls .dk-more { min-height: 48px; border-radius: 8px; padding: 8px 12px; }
|
||||
html[data-layout="classic"] #nowPlayingMeta { margin-top: 24px; gap: 18px; }
|
||||
html[data-layout="classic"] #nowPlayingMeta .np-title { font-size: clamp(22px, 2vw, 30px); line-height: 1.25; letter-spacing: -0.02em; overflow-wrap: anywhere; }
|
||||
html[data-layout="classic"] #nowPlayingMeta .np-channel { color: var(--text-2); font-size: 14px; }
|
||||
html[data-layout="classic"] #nowPlayingMeta .np-actions :is(.np-btn, #saveBtn, #queueBtn, #addPlaylistBtn, #addBtn, #serviceBtn) { flex: 1 1 120px; min-height: 44px; height: auto; padding: 10px 12px; border-radius: 8px; font-size: 13px; background: var(--bg-2); color: var(--text); box-shadow: none; white-space: normal; }
|
||||
html[data-layout="classic"] #nowPlayingMeta .np-actions #saveBtn.done { color: var(--text); background: var(--bg-3); }
|
||||
html[data-layout="classic"] .np-more { border-radius: 8px; min-height: 44px; background: transparent; box-shadow: none; }
|
||||
html[data-layout="classic"] :is(.notes-panel, .upnext, .related-panel) { border-radius: 12px; background: var(--bg-1); box-shadow: none; margin-top: 24px; }
|
||||
html[data-layout="classic"] .notes-tabs { flex-wrap: wrap; padding: 8px; gap: 4px; border-bottom: 1px solid var(--line-soft); }
|
||||
html[data-layout="classic"] .notes-tab { min-height: 44px; padding: 8px 10px; font: 600 13px var(--ui); letter-spacing: 0; text-transform: none; border-radius: 8px; }
|
||||
html[data-layout="classic"] .notes-tab.active { color: var(--text); background: var(--bg-3); box-shadow: none; }
|
||||
html[data-layout="classic"] .lyr-line.active { background: var(--bg-3); box-shadow: none; color: var(--accent-bright); }
|
||||
html[data-layout="classic"] .list-pane { flex: 0 0 clamp(340px, 29%, 420px); width: auto; min-width: 0; min-height: 0; background: var(--bg-1); overflow-y: auto; padding-bottom: var(--classic-mini-height); }
|
||||
html[data-layout="classic"] .list-header { padding: 24px 20px 16px; gap: 16px; }
|
||||
html[data-layout="classic"] .list-header h2 { font-size: 26px; line-height: 1.2; }
|
||||
html[data-layout="classic"] #listActions button { min-height: 44px; height: auto; border-radius: 8px; padding: 8px 12px; }
|
||||
html[data-layout="classic"] #listActions button.la-primary { background: var(--accent-deep); color: #fff; box-shadow: none; font-size: 14px; }
|
||||
html[data-layout="classic"] :is(.list-icon-btn, .list-title-actions .list-icon-btn) { width: 44px; height: 44px; border-radius: 8px; }
|
||||
html[data-layout="classic"] .cards { flex: none; overflow: visible; padding: 4px 12px 24px; gap: 0; }
|
||||
html[data-layout="classic"] .card { display: grid; grid-template-columns: 88px minmax(0, 1fr) 44px; padding: 12px 8px; gap: 0 12px; border-radius: 0; border: 0; border-bottom: 1px solid var(--line-soft); animation: none; box-shadow: none; }
|
||||
html[data-layout="classic"] .card .thumb { grid-column: 1; grid-row: 1 / span 2; width: 88px; }
|
||||
html[data-layout="classic"] .card-info { grid-column: 2; grid-row: 1 / span 2; align-self: center; }
|
||||
html[data-layout="classic"] .card-menu { grid-column: 3; grid-row: 1; }
|
||||
html[data-layout="classic"] .card-del { grid-column: 3; grid-row: 2; }
|
||||
html[data-layout="classic"] .batch-check { position: absolute; left: 8px; top: 12px; z-index: 2; }
|
||||
html[data-layout="classic"] .card.playing { background: var(--bg-3); box-shadow: inset 2px 0 var(--accent); }
|
||||
html[data-layout="classic"] .card.playing::after { display: none; }
|
||||
html[data-layout="classic"] .thumb { width: 100px; border-radius: 6px; align-self: flex-start; }
|
||||
html[data-layout="classic"] .card:hover .thumb img { transform: none; }
|
||||
html[data-layout="classic"] .card-title { font-size: 14px; line-height: 1.45; }
|
||||
html[data-layout="classic"] .card-channel { font-size: 12px; color: var(--text-dim); }
|
||||
html[data-layout="classic"] :is(.card-menu, .card-del) { min-width: 44px; min-height: 44px; opacity: 1; }
|
||||
html[data-layout="classic"] .settings { padding: 8px 20px 28px; overflow: visible; }
|
||||
html[data-layout="classic"] .settings :is(input:not([type="checkbox"]):not([type="range"]), select, button) { min-height: 44px; max-width: 100%; }
|
||||
html[data-layout="classic"] :is(.modal, .shortcut-panel, .more-sheet) { background: var(--bg-1); border-radius: 12px; max-width: 100%; box-shadow: var(--shadow); }
|
||||
html[data-layout="classic"] :is(.modal, .more-sheet) button { min-height: 44px; }
|
||||
html[data-layout="classic"] .mini-bar { left: 232px; bottom: 0; background: var(--bg-2); border-top: 1px solid var(--line); padding: 0; }
|
||||
html[data-layout="classic"] .mini-bar-inner { background: var(--bg-2); border-radius: 0; box-shadow: none; backdrop-filter: none; padding: 10px 20px; }
|
||||
html[data-layout="classic"] .mini-bar button { min-width: 44px; min-height: 44px; }
|
||||
html[data-layout="classic"] .bottom-nav { display: none; background: var(--bg-1); backdrop-filter: none; }
|
||||
html[data-layout="classic"] .bottom-nav-icon { background: none; border-radius: 0; box-shadow: none; }
|
||||
html[data-layout="classic"] .bottom-nav-btn.active .bottom-nav-icon { background: none; color: var(--accent); box-shadow: none; }
|
||||
html[data-layout="classic"] .bottom-nav-label { font: 500 11px var(--ui); letter-spacing: 0; }
|
||||
html[data-layout="classic"] .sec-rail { top: auto; left: 50%; right: auto; bottom: calc(var(--classic-nav-height) + var(--classic-mini-height)); transform: translateX(-50%); flex-direction: row; flex-wrap: wrap; justify-content: center; width: max-content; max-width: calc(100% - 24px); gap: 4px; padding: 4px; border: 0; border-radius: 8px 8px 0 0; background: var(--bg-2); box-shadow: none; backdrop-filter: none; }
|
||||
html[data-layout="classic"] body:has(#miniBar:not(.hidden)) .sec-rail { bottom: calc(var(--classic-nav-height) + var(--classic-mini-height)); transform: translateX(-50%); }
|
||||
html[data-layout="classic"] .sec-rail-btn { width: 44px; height: 44px; border-radius: 8px; }
|
||||
html[data-layout="classic"] .sec-rail-label { right: auto; bottom: 48px; }
|
||||
html[data-layout="classic"] .toast-container { bottom: calc(var(--classic-clearance) + 16px); }
|
||||
html[data-layout="classic"] .empty-state { animation: none; padding: 32px 20px; }
|
||||
html[data-layout="classic"] .empty-cta { background: var(--accent-deep); color: #fff; min-height: 44px; box-shadow: none; transform: none; }
|
||||
html[data-layout="classic"][data-density="compact"] { --classic-space: 16px; }
|
||||
html[data-layout="classic"][data-density="compact"] .card { padding-block: 6px; }
|
||||
html[data-layout="classic"][data-density="compact"] .thumb { width: 88px; }
|
||||
|
||||
@media (min-width: 1201px) {
|
||||
html[data-layout="classic"] .player-pane:not(.empty) .player-stage { width: min(100%, calc(42dvh * 16 / 9)); align-self: center; }
|
||||
}
|
||||
@container (min-width: 620px) {
|
||||
html[data-layout="classic"] #controls { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px 24px; }
|
||||
html[data-layout="classic"] #controls :is(.seek-row, .dk-quick) { grid-column: 1 / -1; }
|
||||
html[data-layout="classic"] #controls .deck-main { gap: 8px; justify-content: start; }
|
||||
html[data-layout="classic"] #controls .deck-main .ctrl { width: 44px; height: 44px; }
|
||||
html[data-layout="classic"] #controls .deck-main .ctrl.play { width: 56px; height: 56px; }
|
||||
}
|
||||
|
||||
@container (max-width: 300px) {
|
||||
html[data-layout="classic"] #controls .deck-main { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 0; }
|
||||
html[data-layout="classic"] #controls .deck-main .ctrl,
|
||||
html[data-layout="classic"] #controls .deck-main .ctrl.play { width: 44px; height: 44px; }
|
||||
}
|
||||
|
||||
@media (max-width: 420px) {
|
||||
html[data-layout="classic"][data-font="xl"] .bottom-nav { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||
}
|
||||
|
||||
/* CSS owns the stacked breakpoint; JS reads this flag for scrolling. */
|
||||
@media (max-width: 1200px) {
|
||||
html[data-layout="classic"] { --classic-stacked: 1; }
|
||||
html[data-layout="classic"] .main { padding-bottom: var(--classic-clearance); }
|
||||
html[data-layout="classic"] .body { flex-direction: column; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; scroll-padding-block: 16px; padding-bottom: 24px; }
|
||||
html[data-layout="classic"] .player-pane { flex: none; width: 100%; overflow: visible; padding-bottom: 24px; }
|
||||
html[data-layout="classic"] .list-pane { flex: none; width: 100%; min-height: 100%; overflow: visible; border-left: 0; border-top: 1px solid var(--line-soft); padding-bottom: 24px; }
|
||||
html[data-layout="classic"] .cards { max-height: none; overflow: visible; }
|
||||
html[data-layout="classic"] .player-placeholder { padding-block: 32px; }
|
||||
}
|
||||
@media (max-width: 860px) {
|
||||
html[data-layout="classic"] { --classic-space: 16px; }
|
||||
html[data-layout="classic"] .app { grid-template-columns: minmax(0, 1fr); }
|
||||
html[data-layout="classic"] .topbar { padding: calc(10px + env(safe-area-inset-top)) max(12px, env(safe-area-inset-right)) 10px max(12px, env(safe-area-inset-left)); gap: 8px; }
|
||||
html[data-layout="classic"] .search-form { gap: 6px; }
|
||||
html[data-layout="classic"] .search-btn { padding-inline: 12px; font-size: 13px; }
|
||||
html[data-layout="classic"] #searchInput { padding-inline: 10px; }
|
||||
html[data-layout="classic"] .sidebar { padding-top: calc(24px + env(safe-area-inset-top)); padding-bottom: calc(24px + env(safe-area-inset-bottom)); z-index: 350; }
|
||||
html[data-layout="classic"] .sidebar-backdrop { z-index: 340; }
|
||||
html[data-layout="classic"] .player-pane { padding-inline: max(16px, env(safe-area-inset-left)) max(16px, env(safe-area-inset-right)); }
|
||||
html[data-layout="classic"] .player-placeholder { padding: 24px 8px 32px; }
|
||||
html[data-layout="classic"] .hero { gap: 24px; }
|
||||
html[data-layout="classic"] .hero-title { font-size: 42px; }
|
||||
html[data-layout="classic"] .hero-tagline { font-size: 16px; }
|
||||
html[data-layout="classic"] .hero-pills { gap: 8px 12px; }
|
||||
html[data-layout="classic"] .home-pl-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
html[data-layout="classic"] .home-pl-title { font-size: 26px; }
|
||||
html[data-layout="classic"] .bottom-nav { display: flex; padding: 6px env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left); }
|
||||
html[data-layout="classic"] .bottom-nav-btn { min-width: 44px; min-height: 58px; padding: 6px 0 8px; gap: 5px; color: var(--text-dim); }
|
||||
html[data-layout="classic"] .bottom-nav-btn.active { color: var(--text); }
|
||||
html[data-layout="classic"] .mini-bar { left: 0; bottom: var(--classic-nav-height); }
|
||||
html[data-layout="classic"] .mini-bar-inner { padding: 8px max(12px, env(safe-area-inset-right)) 8px max(12px, env(safe-area-inset-left)); gap: 8px; }
|
||||
html[data-layout="classic"] .list-header { padding: 24px 16px 16px; }
|
||||
html[data-layout="classic"] .settings { padding-inline: 16px; }
|
||||
html[data-layout="classic"] :is(.modal-backdrop, .shortcut-overlay) { padding: 16px; }
|
||||
html[data-layout="classic"] .shortcut-panel { min-width: 0; width: 100%; padding: 24px; }
|
||||
}
|
||||
@media (max-width: 380px) {
|
||||
html[data-layout="classic"] .thumb { width: 88px; }
|
||||
}
|
||||
Reference in New Issue
Block a user