From 2af2eebaccdafcafe7c42558bcc7450c0dc26f5a Mon Sep 17 00:00:00 2001 From: Jonathan Sykes Date: Thu, 2 Jul 2026 22:38:14 +0800 Subject: [PATCH] feat: redesign portrait PWA as mobile app UI with thumb-zone transport, bottom-sheet modals, and floating mini-player --- frontend/styles.css | 247 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) diff --git a/frontend/styles.css b/frontend/styles.css index ebb15d6..5e950d6 100755 --- a/frontend/styles.css +++ b/frontend/styles.css @@ -1878,6 +1878,253 @@ input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.25); } .hero-tagline { font-size: 13px; } } +/* ============================================================================ + * PORTRAIT PWA — mobile app design refresh + * + * Same media query as the structural block above; being later in the cascade, + * these rules win ties. Scope is intentionally identical so desktop, browser + * tabs, and landscape are never affected. Design goals (mobile-app UI): + * • 44px minimum tap targets, thumb-zone transport controls + * • music-app control deck: centered shuffle/prev/play/next/repeat row, + * secondary tools on a second row; volume slider hidden (hardware keys) + * • bottom-sheet modals, floating mini-player card, nav active pill + * • 16px input font to stop iOS focus-zoom + * ========================================================================== */ +@media (display-mode: standalone) and (orientation: portrait) { + + /* --- Global touch polish ---------------------------------------------- */ + ::-webkit-scrollbar { width: 0; height: 0; } + button, .card, .nav-item, .playlist-item { -webkit-tap-highlight-color: transparent; } + + /* --- Topbar: app-bar with pill search --------------------------------- */ + .sidebar-toggle { + width: 44px; + height: 44px; + border-radius: 14px; + } + .search-form { min-width: 0; } /* flex item: allow shrinking to the viewport */ + #searchInput { + height: 44px; + min-width: 0; /* let the pill shrink below intrinsic input width */ + border-radius: 22px; + padding: 0 18px; + font-size: 16px; /* ≥16px prevents iOS focus-zoom */ + } + .search-btn { + height: 44px; + border-radius: 22px; + padding: 0 18px; + } + + /* --- Player pane ------------------------------------------------------- */ + .player-pane { + padding: 12px calc(12px + env(safe-area-inset-right)) 12px + calc(12px + env(safe-area-inset-left)); + } + .player-stage { border-radius: 16px; } + + /* --- Control deck: two-row music-app transport ------------------------- */ + .controls { + margin-top: 12px; + border-radius: 18px; + padding: 14px 14px 16px; + } + .seek-row { gap: 10px; } + /* Fatter seek track + thumb for touch */ + .seek { height: 6px; } + .seek::-webkit-slider-thumb { width: 18px; height: 18px; } + + .btn-row { + justify-content: center; + gap: 10px 8px; + margin-top: 14px; + } + /* Forced line break between transport row (order ≤ 5) and tool row (≥ 7) */ + .btn-row::before { content: ""; width: 100%; order: 6; height: 0; } + + /* Row A — transport, centered in the thumb zone */ + #shuffleBtn { order: 1; } + #prevBtn { order: 2; } + #playBtn { order: 3; } + #nextBtn { order: 4; } + #repeatBtn { order: 5; } + + #prevBtn, #nextBtn, #shuffleBtn, #repeatBtn { + background: transparent; + border-color: transparent; + width: 48px; + height: 48px; + border-radius: 50%; + } + #prevBtn, #nextBtn { font-size: 20px; } + #shuffleBtn, #repeatBtn { font-size: 17px; color: var(--text-2); } + #shuffleBtn.active, #repeatBtn.active { + color: var(--accent-bright); + background: rgba(255, 75, 50, 0.16); + border-color: transparent; + box-shadow: none; + } + #playBtn { + width: 64px; + height: 64px; + border-radius: 50%; + font-size: 24px; + margin: 0 6px; + } + + /* Row B — secondary tools, uniform 44px targets (sizes only: colors and + .active/.sleep-active states keep their class-based styling) */ + #abABtn, #abBBtn, #abClearBtn, #muteBtn, #loopBtn, #sleepTimerBtn, #fsBtn { + order: 7; + min-width: 44px; + height: 44px; + border-radius: 13px; + } + .vol { order: 7; } + .vol input { display: none; } /* hardware volume keys on mobile */ + .btn-row .spacer { display: none; } + .btn-row .sel { order: 8; } + .sel select { + height: 44px; + border-radius: 13px; + padding: 0 10px; + } + + /* --- Now playing meta: full-width action grid --------------------------- */ + .now-meta { margin-top: 16px; } + .np-title { font-size: 18px; } + .np-actions { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 8px; + width: 100%; + } + .np-btn { + padding: 12px 8px; + text-align: center; + border-radius: 13px; + } + + /* --- Up next / related: comfier touch rows ------------------------------ */ + .upnext { border-radius: 18px; } + .upnext-item { padding: 8px; border-radius: 12px; } + .upnext-item img { width: 96px; border-radius: 8px; } + .related-panel { border-radius: 18px; } + .related-item { padding: 8px; border-radius: 12px; } + .related-item img { width: 96px; border-radius: 8px; } + + /* --- List pane: large title, touch-sized rows --------------------------- */ + .list-header { padding: 18px 16px 10px; } + .list-header h2 { font-size: 24px; letter-spacing: -0.02em; } + .list-actions button { padding: 9px 13px; border-radius: 10px; } + .list-filter-bar input { + padding: 11px 14px; + border-radius: 13px; + font-size: 16px; + } + .batch-btn { padding: 10px 14px; border-radius: 10px; } + + .cards { gap: 8px; padding-left: 12px; padding-right: 12px; } + .card { padding: 10px; gap: 12px; border-radius: 16px; } + .thumb { border-radius: 12px; } + .card-title { font-size: 14px; } + .card-channel { margin-top: 6px; } + /* No hover on touch — keep row actions always visible and finger-sized */ + .card-menu { width: 38px; height: 38px; border-radius: 12px; opacity: 1; } + .card-del { opacity: 1; width: 36px; height: 36px; border-radius: 11px; } + + /* --- Settings: roomier rows --------------------------------------------- */ + .set-group { border-radius: 18px; } + .set-row { padding: 14px 0; } + .set-row input[type="checkbox"] { width: 22px; height: 22px; } + .set-select { padding: 10px 12px; border-radius: 11px; } + + /* --- Sidebar drawer: rounded sheet edge --------------------------------- */ + .sidebar { + border-right: none; + border-radius: 0 22px 22px 0; + } + .nav-item { padding: 13px 14px; font-size: 15px; } + .playlist-item { padding: 12px 14px; } + .switch { padding: 12px 10px; } + + /* --- Bottom nav: active pill indicator ---------------------------------- */ + .bottom-nav-btn { padding: 8px 4px 10px; gap: 4px; } + .bottom-nav-icon { + font-size: 21px; + padding: 4px 16px; + border-radius: 16px; + transition: background 0.2s var(--ease), transform 0.18s var(--ease); + } + .bottom-nav-btn.active .bottom-nav-icon { + background: rgba(255, 75, 50, 0.16); + transform: none; + } + .bottom-nav-badge { top: 4px; margin-left: 10px; } + + /* --- Mini player: floating card above the nav --------------------------- */ + .mini-bar { + left: 10px; + right: 10px; + bottom: calc(72px + env(safe-area-inset-bottom)); + border-radius: 16px; + overflow: hidden; + border: 1px solid var(--line); + box-shadow: 0 12px 34px -10px rgba(0, 0, 0, 0.65); + } + .mini-bar-inner { + border-top: none; + padding: 10px 6px 10px 14px; + } + .mini-btn { width: 40px; height: 40px; border-radius: 12px; } + .mini-close { width: 36px; height: 36px; } + + /* List pane clearance for taller nav + floating mini-bar */ + .list-pane { padding-bottom: calc(152px + env(safe-area-inset-bottom)); } + + /* --- Toasts: clear the floating mini-bar -------------------------------- */ + .toast-container { bottom: calc(148px + env(safe-area-inset-bottom)); } + .toast { white-space: normal; max-width: calc(100vw - 32px); text-align: center; } + + /* --- Modals: bottom sheet ----------------------------------------------- */ + .modal-backdrop { + align-items: flex-end; + padding: 0; + z-index: 400; /* above mini-bar (300) and bottom-nav (310) */ + } + .modal { + width: 100%; + max-width: 100%; + border-radius: 22px 22px 0 0; + border-left: none; + border-right: none; + border-bottom: none; + padding: 12px 20px calc(24px + env(safe-area-inset-bottom)); + animation: sheetUp 0.3s var(--ease); + } + .modal::before { + content: ""; + display: block; + width: 40px; + height: 4px; + border-radius: 2px; + background: var(--line); + margin: 0 auto 16px; + } + .modal input[type="text"] { font-size: 16px; padding: 13px 15px; border-radius: 13px; } + .modal-list button { padding: 14px 15px; border-radius: 13px; } + .modal-actions { margin-top: 22px; } + .modal-actions .btn { padding: 13px 20px; border-radius: 13px; } + + /* --- Shortcut overlay: fit narrow screens ------------------------------- */ + .shortcut-panel { min-width: 0; width: 100%; max-width: 480px; } +} + +@keyframes sheetUp { + from { opacity: 0; transform: translateY(40%); } + to { opacity: 1; transform: none; } +} + /* ============================================================================ * PORTRAIT PWA — JS-class mirror (.portrait-pwa on .app) *