feat: side bar playlist and other items not present on some screensizes. make completely responsive

Task #40 completed by ClaudeQueue

ClaudeQueue
This commit is contained in:
ClaudeQueue
2026-06-30 01:06:54 +00:00
parent f266c4ffb9
commit 05e78677b0
3 changed files with 123 additions and 5 deletions

View File

@@ -1741,6 +1741,8 @@ function render() {
listFilter = '';
const fi = $('listFilterInput');
if (fi) fi.value = '';
// On mobile, navigating (nav item / playlist / search) dismisses the drawer.
if (typeof closeSidebar === 'function') closeSidebar();
exitSelectMode();
renderSidebar();
renderSmartSidebar();
@@ -1977,7 +1979,38 @@ function closeModal() { $('modal').classList.add('hidden'); }
// ============================================================================
// Events
// ============================================================================
// ============================================================================
// Responsive sidebar drawer (mobile)
// ============================================================================
function openSidebar() {
const app = document.querySelector('.app');
if (!app) return;
app.classList.add('sidebar-open');
const t = $('sidebarToggle');
if (t) t.setAttribute('aria-expanded', 'true');
}
function closeSidebar() {
const app = document.querySelector('.app');
if (!app) return;
app.classList.remove('sidebar-open');
const t = $('sidebarToggle');
if (t) t.setAttribute('aria-expanded', 'false');
}
function setupSidebarDrawer() {
const toggle = $('sidebarToggle');
const backdrop = $('sidebarBackdrop');
if (toggle) {
toggle.addEventListener('click', () => {
const app = document.querySelector('.app');
if (app && app.classList.contains('sidebar-open')) closeSidebar();
else openSidebar();
});
}
if (backdrop) backdrop.addEventListener('click', closeSidebar);
}
function wireUI() {
setupSidebarDrawer();
els.searchForm.addEventListener('submit', async (e) => {
e.preventDefault();
const q = els.searchInput.value.trim();
@@ -2233,7 +2266,9 @@ document.querySelectorAll('.chip').forEach((c) => {
else if (e.key === 'b') { if (current) setAbB(); }
else if (e.key === '?') toggleShortcutHelp();
else if (e.key === 'Escape') {
if (!$('shortcutHelp').classList.contains('hidden')) $('shortcutHelp').classList.add('hidden');
const app = document.querySelector('.app');
if (app && app.classList.contains('sidebar-open')) closeSidebar();
else if (!$('shortcutHelp').classList.contains('hidden')) $('shortcutHelp').classList.add('hidden');
else if (selectMode) { exitSelectMode(); renderList(); }
}
});

View File

@@ -25,7 +25,7 @@
<body>
<div class="app">
<!-- Sidebar -->
<aside class="sidebar">
<aside id="sidebar" class="sidebar">
<div class="brand">
<span class="brand-mark"></span> YT Player
</div>
@@ -58,9 +58,20 @@
</div>
</aside>
<!-- Sidebar backdrop (mobile drawer) -->
<div id="sidebarBackdrop" class="sidebar-backdrop"></div>
<!-- Main -->
<main class="main">
<header class="topbar">
<button
id="sidebarToggle"
class="sidebar-toggle"
type="button"
aria-label="Toggle sidebar"
aria-controls="sidebar"
aria-expanded="false"
></button>
<form id="searchForm" class="search-form">
<input
id="searchInput"

View File

@@ -90,7 +90,8 @@ body::after {
display: flex;
flex-direction: column;
padding: 22px 14px 16px;
overflow: hidden;
overflow-y: auto;
overflow-x: hidden;
}
.brand {
@@ -200,7 +201,7 @@ body::after {
border-radius: 20px;
}
.sidebar-footer { border-top: 1px solid var(--line-soft); padding-top: 12px; margin-top: 10px; }
.sidebar-footer { border-top: 1px solid var(--line-soft); padding-top: 12px; margin-top: auto; flex-shrink: 0; }
.switch {
display: flex; align-items: center; gap: 10px;
color: var(--text-2); cursor: pointer; padding: 8px 10px;
@@ -214,12 +215,45 @@ body::after {
.main { display: flex; flex-direction: column; overflow: hidden; }
.topbar {
display: flex;
align-items: center;
gap: 12px;
padding: 16px 26px;
border-bottom: 1px solid var(--line-soft);
background: linear-gradient(180deg, rgba(16,16,19,0.7), transparent);
backdrop-filter: blur(8px);
}
.search-form { display: flex; gap: 10px; max-width: 760px; }
.search-form { flex: 1; display: flex; gap: 10px; max-width: 760px; }
/* Hamburger toggle — hidden on desktop, revealed in the mobile breakpoint */
.sidebar-toggle {
display: none;
flex-shrink: 0;
width: 42px;
height: 42px;
align-items: center;
justify-content: center;
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: 11px;
color: var(--text);
font-size: 18px;
line-height: 1;
cursor: pointer;
transition: background 0.18s, border-color 0.18s;
}
.sidebar-toggle:hover { background: var(--bg-3); border-color: var(--accent); }
/* Backdrop behind the off-canvas drawer — only shown when the drawer is open */
.sidebar-backdrop {
display: none;
position: fixed;
inset: 0;
z-index: 90;
background: rgba(0, 0, 0, 0.5);
opacity: 0;
transition: opacity 0.25s var(--ease);
}
#searchInput {
flex: 1;
background: var(--bg-2);
@@ -1179,6 +1213,44 @@ input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.25); }
.list-pane { width: auto; border-left: none; border-top: 1px solid var(--line-soft); }
}
/* ============================================================================
* Responsive sidebar — off-canvas drawer on small screens
* Below 860px the fixed 264px grid column is dropped; the sidebar becomes a
* slide-in drawer toggled by the hamburger button so the playlist and every
* other sidebar item stay reachable from 320px up to tablet width.
* ========================================================================== */
@media (max-width: 860px) {
.app { grid-template-columns: 1fr; }
.sidebar-toggle { display: inline-flex; }
.sidebar {
position: fixed;
top: 0;
left: 0;
bottom: 0;
width: min(86vw, 300px);
max-width: 300px;
z-index: 100;
border-right: 1px solid var(--line-soft);
box-shadow: var(--shadow);
transform: translateX(-100%);
transition: transform 0.28s var(--ease);
will-change: transform;
}
.app.sidebar-open .sidebar { transform: translateX(0); }
.app.sidebar-open .sidebar-backdrop { display: block; opacity: 1; }
}
/* Very narrow screens (e.g. 320px) — keep the topbar usable and the drawer
from ever exceeding the viewport. */
@media (max-width: 420px) {
.topbar { padding: 12px 14px; }
.sidebar { width: min(88vw, 280px); }
}
/* ============================================================================
* Loop / repeat active state
* ========================================================================== */