The old mobile treatment just reshaped the sidebar into a horizontally scrolling top bar - easy to miss that there's more to scroll to, and "Se deconnecter" ended up buried at the far end of it. Explored as mockups first (compared against a bottom tab bar, a hamburger drawer, and a polished version of the existing bar) before writing this. The sheet mirrors the real data shape instead of a flat list: Accueil and Listes de lecture (when a playlists-type library exists) are fixed rows bookending a Bibliotheques group, which is the part that actually varies with whatever the connected Emby server returns - zero new server work, since getLibraries() already sends collectionType per library, just unused on the client until now. The music bar's stacking under the new nav trigger is now structural (a shared position:fixed flex column) instead of a hardcoded offset, since the bar's own height isn't constant - its row can wrap on a narrow screen with a long track/artist name. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
223 lines
4.8 KiB
CSS
223 lines
4.8 KiB
CSS
/* ---- App shell: sidebar + routed main content - see Shell.ts. ---- */
|
|
|
|
.appShell {
|
|
display: flex;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.sidebar {
|
|
width: 220px;
|
|
flex-shrink: 0;
|
|
background: var(--bg-header);
|
|
padding: 20px 12px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
position: sticky;
|
|
top: 0;
|
|
height: 100vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.sidebarBrand {
|
|
font-size: 18px;
|
|
font-weight: 700;
|
|
padding: 0 12px 20px;
|
|
}
|
|
|
|
.sidebarNav {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
flex: 1;
|
|
}
|
|
|
|
.navSectionLabel {
|
|
color: var(--muted);
|
|
font-size: 11px;
|
|
text-transform: uppercase;
|
|
letter-spacing: .05em;
|
|
padding: 16px 12px 6px;
|
|
}
|
|
|
|
.navItem {
|
|
display: block;
|
|
padding: 10px 12px;
|
|
border-radius: 8px;
|
|
color: var(--text);
|
|
text-decoration: none;
|
|
font-size: 14px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.navItem:hover { background: rgba(255, 255, 255, .06); }
|
|
.navItem.active { background: var(--accent); color: white; font-weight: 600; }
|
|
|
|
.sidebarLogout { margin-top: 12px; padding: 8px 12px; }
|
|
|
|
.mainContent {
|
|
flex: 1;
|
|
min-width: 0;
|
|
padding: 24px clamp(16px, 3vw, 48px) 80px;
|
|
}
|
|
|
|
/* ==== Mobile navigation (<=720px) ====
|
|
Replaces the sidebar outright - see docs/architecture.md's "Mobile
|
|
navigation" section for why a bottom sheet was chosen over reshaping
|
|
the sidebar into a horizontal bar. Every rule below the base
|
|
`display: none` only ever applies once the 720px media query turns
|
|
it back on, so none of this affects desktop. */
|
|
.mobileTopBar,
|
|
.mobileNavTrigger,
|
|
.mobileNavScrim,
|
|
.mobileNavSheet {
|
|
display: none;
|
|
}
|
|
|
|
.mobileTopBar {
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 3;
|
|
padding: 14px 16px;
|
|
background: var(--bg-header);
|
|
}
|
|
|
|
.mobileBrand { font-size: 16px; font-weight: 700; }
|
|
|
|
/* position:fixed, not sticky - #musicBar below already relies on a
|
|
fixed wrapper to stay docked regardless of how far #mainContent's
|
|
own (whole-page, not internally-scrolled) content runs. */
|
|
.bottomStack {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
z-index: 15;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.mobileNavTrigger {
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
background: var(--bg-header);
|
|
border: none;
|
|
border-top: 1px solid rgba(255, 255, 255, .08);
|
|
color: var(--text);
|
|
font-family: inherit;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
padding: 13px;
|
|
padding-bottom: calc(13px + env(safe-area-inset-bottom, 0px));
|
|
}
|
|
|
|
.mobileNavScrim {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 20;
|
|
background: rgba(0, 0, 0, .5);
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
transition: opacity .2s;
|
|
}
|
|
|
|
.mobileNavScrim.open { opacity: 1; pointer-events: auto; }
|
|
|
|
.mobileNavSheet {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
z-index: 21;
|
|
max-height: 75vh;
|
|
overflow-y: auto;
|
|
background: var(--bg-header);
|
|
border-radius: 16px 16px 0 0;
|
|
padding: 8px 16px calc(20px + env(safe-area-inset-bottom, 0px));
|
|
transform: translateY(100%);
|
|
transition: transform .22s ease;
|
|
box-shadow: 0 -8px 24px rgba(0, 0, 0, .3);
|
|
}
|
|
|
|
.mobileNavSheet.open { transform: translateY(0); }
|
|
|
|
.sheetGrabber {
|
|
width: 36px;
|
|
height: 4px;
|
|
border-radius: 99px;
|
|
background: var(--muted);
|
|
margin: 10px auto 6px;
|
|
}
|
|
|
|
/* Accueil and the (conditional) Listes de lecture row both use this -
|
|
same visual weight, bookending the "Bibliothèques" group between
|
|
them, so the one part whose size actually varies (however many
|
|
libraries Emby returns) reads as visually distinct from the two
|
|
fixed rows around it. */
|
|
.sheetPrimary {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
width: 100%;
|
|
background: var(--panel);
|
|
border: none;
|
|
border-radius: 10px;
|
|
color: var(--text);
|
|
font-family: inherit;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
padding: 12px;
|
|
margin: 4px 0;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.sheetPrimary.active { background: var(--accent); color: #fff; }
|
|
|
|
.sheetGrid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 8px;
|
|
margin: 4px 0 10px;
|
|
}
|
|
|
|
.sheetItem {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 9px;
|
|
background: var(--panel);
|
|
border-radius: 10px;
|
|
color: var(--text);
|
|
font-size: 13px;
|
|
padding: 12px 10px;
|
|
text-decoration: none;
|
|
}
|
|
|
|
.sheetItem.active { background: var(--accent); color: #fff; font-weight: 600; }
|
|
|
|
.navIcon { width: 20px; height: 20px; flex-shrink: 0; display: block; }
|
|
.navIcon svg { width: 100%; height: 100%; }
|
|
|
|
.sheetLogout {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
width: 100%;
|
|
margin-top: 8px;
|
|
padding: 12px;
|
|
border: 1px solid rgba(255, 255, 255, .15);
|
|
border-radius: 10px;
|
|
color: var(--muted);
|
|
font-size: 13px;
|
|
}
|
|
|
|
@media (max-width: 720px) {
|
|
.appShell { flex-direction: column; }
|
|
.sidebar { display: none; }
|
|
.mobileTopBar { display: flex; align-items: center; }
|
|
.mobileNavTrigger { display: flex; }
|
|
.mobileNavScrim { display: block; }
|
|
.mobileNavSheet { display: block; }
|
|
}
|