android: no bar arrives over the page — the tab pages lose their top banner
The glass bar was anonymous until you scrolled, then faded in carrying the title the page already had in 27px type two centimetres below it. On Home that meant "Good morning" over "Good morning", half-transparent, while the content it covered kept moving underneath. Kyle's word: remove them. So a tab page has no bar at all. `barOn`/`titled` and the scroll trigger they read are gone; `showBar` is `!root`, and the element is not rendered on Home, Search, Library, Community or You. Nothing fades in over what you are reading. The bar was not only decoration, which is why this is more than a deletion: its ⋯ is the page overflow, and on a tab page that overflow is the app's own menu — the queue, the sleep timer, Settings. That moves to where the page's title already is, in `.phead`, beside Home's identicon and at the end of Library's existing row of actions. Search and You grow the row they did not have. Community already carried its own ⋯ and keeps it. A pushed page still has a bar, because it carries the back chevron and a title that is not repeated anywhere else on the screen. It is static now: rendered only when there is somewhere to go back to, on from the first frame and titled from the first frame. The opacity-0 default, the 180ms cross-fade, and the `.gb-t` rise the title used to arrive on all come out of the stylesheet — nothing is left that can catch the eye mid-scroll. The walk reached that overflow through `.gbar .iconbtn` on a tab page, which is now a selector that matches nothing there: `home-more` on both step lists and the two sleep-timer steps go to `.phead .iconbtn.muted` instead. The other `.gbar` steps are untouched on purpose — the playlist export sheet is a PUSHED page and still has its bar, and `library-ambience` is the album page's back chevron, not an overflow. Checked on the device rather than argued: on Home `.gbar` is absent, the ⋯ is present, and tapping it opens the half sheet on "SoulStream · Queue · Sleep timer off · Settings"; on a pushed record the bar is there with its two buttons, its title, and a computed opacity of exactly 1. One thing this leaves: the bar was also what backed the OS status bar once a page scrolled under it. The clock and the battery now sit on the content. That is fine on the ground and on dark artwork, and it will not be over a white sleeve — a short scrim with no title and no buttons would fix it without bringing a banner back, and is deliberately not in this commit.
This commit is contained in:
1 parent
07dad65ca5
commit
cdb861fcbd
7 files changed
+26
-28
No files matched your search
+13
-10
@@ -117,9 +117,10 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
const groundPath = $derived(meta.amb ?? player.album?.art_path ?? null);
|
const groundPath = $derived(meta.amb ?? player.album?.art_path ?? null);
|
||||||
const trigger = $derived(meta.hero ? 210 : 52);
|
/* A tab page has no bar at all: nothing fades in over what you are
|
||||||
const barOn = $derived(!root || scrolled > trigger);
|
reading. A pushed page has one because it carries the back chevron,
|
||||||
const titled = $derived(scrolled > trigger);
|
and it is static — there and titled from the first frame. */
|
||||||
|
const showBar = $derived(!root);
|
||||||
const playing = $derived(player.status === "playing" || player.status === "buffering");
|
const playing = $derived(player.status === "playing" || player.status === "buffering");
|
||||||
const played = $derived(player.duration ? Math.min(1, player.position / player.duration) : 0);
|
const played = $derived(player.duration ? Math.min(1, player.position / player.duration) : 0);
|
||||||
|
|
||||||
@@ -382,13 +383,15 @@
|
|||||||
{/key}
|
{/key}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- the glass bar: GlassBar.svelte's recipe; anonymous until the
|
<!-- the bar a pushed page carries: back, the title, and the page's ⋯.
|
||||||
page's own large title has scrolled under it -->
|
A tab page has none — see showBar. -->
|
||||||
<div class="gbar" class:on={barOn} class:titled>
|
{#if showBar}
|
||||||
<button class="iconbtn" style:visibility={root ? "hidden" : "visible"} onclick={back} aria-label="Back"><Icon name="chevron-left" size={24} /></button>
|
<div class="gbar">
|
||||||
<span class="gb-t">{meta.title}{#if meta.kicker && meta.kicker !== meta.title}<small>{meta.kicker}</small>{/if}</span>
|
<button class="iconbtn" onclick={back} aria-label="Back"><Icon name="chevron-left" size={24} /></button>
|
||||||
<button class="iconbtn muted" onclick={() => openHalf("more")} aria-label="More"><Icon name="more" size={21} /></button>
|
<span class="gb-t">{meta.title}{#if meta.kicker && meta.kicker !== meta.title}<small>{meta.kicker}</small>{/if}</span>
|
||||||
</div>
|
<button class="iconbtn muted" onclick={() => openHalf("more")} aria-label="More"><Icon name="more" size={21} /></button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if player.track}
|
{#if player.track}
|
||||||
|
|||||||
@@ -150,9 +150,12 @@
|
|||||||
<div class="kick">{dateKick()}</div>
|
<div class="kick">{dateKick()}</div>
|
||||||
<h1>{greeting()}</h1>
|
<h1>{greeting()}</h1>
|
||||||
</div>
|
</div>
|
||||||
<button class="iconbtn" onclick={() => go("you")} aria-label="You">
|
<div class="acts">
|
||||||
<Identicon seed={lib.username || "SoulStream"} size={34} />
|
<button class="iconbtn muted" onclick={() => openHalf("more")} aria-label="More"><Icon name="more" size={21} /></button>
|
||||||
</button>
|
<button class="iconbtn" onclick={() => go("you")} aria-label="You">
|
||||||
|
<Identicon seed={lib.username || "SoulStream"} size={34} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,7 @@
|
|||||||
<button class="iconbtn" onclick={() => go("search")} aria-label="Search"><Icon name="search" size={21} /></button>
|
<button class="iconbtn" onclick={() => go("search")} aria-label="Search"><Icon name="search" size={21} /></button>
|
||||||
<button class="iconbtn" onclick={() => patch({ tab: "playlists" })} aria-label="Playlists"><Icon name="playlist" size={21} /></button>
|
<button class="iconbtn" onclick={() => patch({ tab: "playlists" })} aria-label="Playlists"><Icon name="playlist" size={21} /></button>
|
||||||
<button class="iconbtn" onclick={() => openHalf("librarysort")} aria-label="Sort and filter"><Icon name="layers" size={21} /></button>
|
<button class="iconbtn" onclick={() => openHalf("librarysort")} aria-label="Sort and filter"><Icon name="layers" size={21} /></button>
|
||||||
|
<button class="iconbtn muted" onclick={() => openHalf("more")} aria-label="More"><Icon name="more" size={21} /></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -287,7 +287,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="phead"><div class="row"><h1>Search</h1></div></div>
|
<div class="phead"><div class="row"><h1>Search</h1><div class="acts"><button class="iconbtn muted" onclick={() => openHalf("more")} aria-label="More"><Icon name="more" size={21} /></button></div></div></div>
|
||||||
|
|
||||||
<div class="sfield">
|
<div class="sfield">
|
||||||
<Icon name="search" size={18} />
|
<Icon name="search" size={18} />
|
||||||
|
|||||||
@@ -189,7 +189,7 @@
|
|||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="phead"><div class="row"><h1>You</h1></div></div>
|
<div class="phead"><div class="row"><h1>You</h1><div class="acts"><button class="iconbtn muted" onclick={() => openHalf("more")} aria-label="More"><Icon name="more" size={21} /></button></div></div></div>
|
||||||
|
|
||||||
<div class="sheet-sec" style="padding-top: 4px">Appearance</div>
|
<div class="sheet-sec" style="padding-top: 4px">Appearance</div>
|
||||||
<div class="group">
|
<div class="group">
|
||||||
|
|||||||
@@ -117,11 +117,7 @@
|
|||||||
the solid floor first. */
|
the solid floor first. */
|
||||||
background: rgba(18, 18, 22, 0.92);
|
background: rgba(18, 18, 22, 0.92);
|
||||||
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05);
|
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.05);
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 180ms ease;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
}
|
||||||
.gbar.on { opacity: 1; pointer-events: auto; }
|
|
||||||
html[data-glass="blur"] .gbar { background: rgba(20, 20, 24, 0.28); backdrop-filter: blur(12px) saturate(1.7); -webkit-backdrop-filter: blur(12px) saturate(1.7); }
|
html[data-glass="blur"] .gbar { background: rgba(20, 20, 24, 0.28); backdrop-filter: blur(12px) saturate(1.7); -webkit-backdrop-filter: blur(12px) saturate(1.7); }
|
||||||
html[data-material="solid"] .gbar { background: rgba(24, 24, 24, 0.94); backdrop-filter: none; -webkit-backdrop-filter: none; }
|
html[data-material="solid"] .gbar { background: rgba(24, 24, 24, 0.94); backdrop-filter: none; -webkit-backdrop-filter: none; }
|
||||||
.gbar .gb-t {
|
.gbar .gb-t {
|
||||||
@@ -134,12 +130,7 @@ html[data-material="solid"] .gbar { background: rgba(24, 24, 24, 0.94); backdrop
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
|
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
|
||||||
/* the bar arrives before its title does */
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(5px);
|
|
||||||
transition: opacity 200ms ease, transform 240ms cubic-bezier(0.22, 1, 0.36, 1);
|
|
||||||
}
|
}
|
||||||
.gbar.titled .gb-t { opacity: 1; transform: none; }
|
|
||||||
.gbar .gb-t small {
|
.gbar .gb-t small {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 10.5px;
|
font-size: 10.5px;
|
||||||
|
|||||||
@@ -966,7 +966,7 @@ const MOCK_STEPS = [
|
|||||||
{ do: `__settle()`, wait: 600 },
|
{ do: `__settle()`, wait: 600 },
|
||||||
{ do: `document.querySelectorAll('.nav button')[0].click()`, name: "home-again", wait: 700 },
|
{ do: `document.querySelectorAll('.nav button')[0].click()`, name: "home-again", wait: 700 },
|
||||||
/* the page overflow → the sleep timer */
|
/* the page overflow → the sleep timer */
|
||||||
{ do: `document.querySelectorAll('.gbar .iconbtn')[1].click()`, name: "home-more", wait: 700 },
|
{ do: `document.querySelector('.phead .iconbtn.muted').click()`, name: "home-more", wait: 700 },
|
||||||
{ do: `__click('.halfsheet .row', 'Sleep timer')`, name: "sleep-sheet", wait: 700 },
|
{ do: `__click('.halfsheet .row', 'Sleep timer')`, name: "sleep-sheet", wait: 700 },
|
||||||
{ do: `__click('.halfsheet .chip', '30 min')`, name: "sleep-set", wait: 700 },
|
{ do: `__click('.halfsheet .chip', '30 min')`, name: "sleep-set", wait: 700 },
|
||||||
/* ── the phone turned over. A resize, so the app keeps playing and keeps
|
/* ── the phone turned over. A resize, so the app keeps playing and keeps
|
||||||
@@ -2105,12 +2105,12 @@ const PHONE_STEPS = [
|
|||||||
died in that gap would leave a pause waiting on his playback — so
|
died in that gap would leave a pause waiting on his playback — so
|
||||||
it is the SHORTEST chip in the sheet, not a comfortable one. */
|
it is the SHORTEST chip in the sheet, not a comfortable one. */
|
||||||
{ do: `document.querySelectorAll('.nav button')[0].click()`, name: "home-feed", wait: 1200 },
|
{ do: `document.querySelectorAll('.nav button')[0].click()`, name: "home-feed", wait: 1200 },
|
||||||
{ do: `document.querySelectorAll('.gbar .iconbtn')[1].click()`, name: "home-more", wait: 900 },
|
{ do: `document.querySelector('.phead .iconbtn.muted').click()`, name: "home-more", wait: 900 },
|
||||||
{ do: `__click('.halfsheet .row', 'Sleep timer')`, name: "sleep-sheet", wait: 800 },
|
{ do: `__click('.halfsheet .row', 'Sleep timer')`, name: "sleep-sheet", wait: 800 },
|
||||||
{ do: `__click('.halfsheet .chip', '15 min')`, name: "sleep-set", wait: 800 },
|
{ do: `__click('.halfsheet .chip', '15 min')`, name: "sleep-set", wait: 800 },
|
||||||
{ do: `document.querySelectorAll('.gbar .iconbtn')[1].click()`, name: "sleep-armed", wait: 900 },
|
{ do: `document.querySelector('.phead .iconbtn.muted').click()`, name: "sleep-armed", wait: 900 },
|
||||||
{ do: `(function () { function pick(sel, re) { return [].slice.call(document.querySelectorAll(sel)).find(function (x) { return re.test((x.textContent || '').trim()); }); } var r = pick('.halfsheet .row', /Sleep timer/); if (r) r.click(); return new Promise(function (res) { setTimeout(function () { var c = pick('.halfsheet .chip', /^Off$/); if (c) c.click(); res(!!c); }, 400); }); })()`, name: "sleep-off", wait: 900 },
|
{ do: `(function () { function pick(sel, re) { return [].slice.call(document.querySelectorAll(sel)).find(function (x) { return re.test((x.textContent || '').trim()); }); } var r = pick('.halfsheet .row', /Sleep timer/); if (r) r.click(); return new Promise(function (res) { setTimeout(function () { var c = pick('.halfsheet .chip', /^Off$/); if (c) c.click(); res(!!c); }, 400); }); })()`, name: "sleep-off", wait: 900 },
|
||||||
{ do: `document.querySelectorAll('.gbar .iconbtn')[1].click()`, name: "sleep-cleared", wait: 900 },
|
{ do: `document.querySelector('.phead .iconbtn.muted').click()`, name: "sleep-cleared", wait: 900 },
|
||||||
{ do: `(function () { var s = document.querySelector('.scrim'); if (s) s.click(); return true; })()`, wait: 600 },
|
{ do: `(function () { var s = document.querySelector('.scrim'); if (s) s.click(); return true; })()`, wait: 600 },
|
||||||
/* You: the stream cache's retention chip, read first and put back
|
/* You: the stream cache's retention chip, read first and put back
|
||||||
last. The value is stashed IN THE PAGE as well as in the harness,
|
last. The value is stashed IN THE PAGE as well as in the harness,
|
||||||
|
|||||||
Reference in new issue
Block a user