android: the page ends at the clock — the bars go, the hero keeps the top edge
The bar was anonymous until you scrolled, then faded in carrying the title the page already had in 27px type two centimetres below it. Kyle's rule for what replaces it: the page is cut off where it meets the Android status bar and dissolves there, and — the part that decides the whole shape of this — **backgrounds may pass under the clock, words may not**. A mask on the scroller cannot tell a photograph from a sentence. It takes both. So the artist hero is painted twice: the page's own copy inside the masked scroller, cut away in that band with everything else, and a second copy behind the scroller (heroart.svelte.js), unmasked, showing through exactly where the mask took the first one away. Same image, same box, same focal point, same scale, offset by the same scroll — one picture to the eye, and only the name dissolves. The second copy carries `overflow: hidden` for the same reason .hero does: without it the 1.04 scale renders 268px in a 258px box and five pixels of photograph appear under the hero's foot, below the black the scrim fades to. That also retires an attempt that did not survive contact. The mask first stepped aside for the hero on a scroll-driven `--fade-on`, which meant rebuilding a mask-image on the scroller every frame; measured at 60 Hz median and p90 either way, but it is work for nothing, and the flip that replaced it put the artist's name out of existence in one step. With the photograph protected by being OUTSIDE the mask, neither is needed: the mask is a constant and nothing recomputes it. No page has a bar now. A tab page's ⋯ — which is the app's own menu, the queue and the sleep timer and Settings — moved beside that page's own large title in `.phead`; Search and You grew the row they did not have, Community kept the one it had. A pushed page floats a back chevron and a ⋯ over the page instead, on flat discs with no backdrop blur, because the bar's own note records what three blurred bands did to a 60 Hz scroll on the Pixel 6a and these would have been a fourth. Three numbers now come from one place. `--chrome-d` and `--chrome-gap` are the disc and its margin; `.screen.has-chrome` reserves exactly that much at the top of a pushed page, and `--fade-top` reaches exactly the chrome's foot. They were independent for one build and it showed immediately: the page began underneath its own buttons, so "Transfers" sat behind the back chevron before you had scrolled at all, and the fade stopped 17px short of the discs so they stood on crisp text and a record's floating ⋯ landed on a track row's own ⋯. The walk followed. Nineteen steps reached a page's overflow through `.gbar .iconbtn`, which now matches nothing: the four on a tab page go to `.phead .iconbtn.muted`, the rest to `.fchrome.more`, and the album's back chevron to `.fchrome.back`. The `title` field read the bar's own text, which is a worse source than it looks — it only existed when chrome happened to be drawn. The screen carries its meta as data attributes now and the three probes read those, spelled exactly as `.gb-t`'s textContent was (the title, then the kicker when it differs) so every expectation written against it still holds. Checked on the device rather than argued: no `.gbar` and no `.gb-t` anywhere, `.phead .iconbtn.muted` present on all four tab pages and opening "SoulStream · Queue · Sleep timer · Settings", both `.fchrome` controls present on a record, and the title probe answering "Random Access MemoriesDaft Punk". One thing this leaves, and it is a real cost rather than an oversight: a tab page's ⋯ scrolls away with its header. The bar used to carry it the whole way down, so on Home the sleep timer now wants a scroll back to the top.
This commit is contained in:
1 parent
cdb861fcbd
commit
1f9b30c51f
6 files changed
+140
-76
No files matched your search
@@ -16,6 +16,7 @@
|
||||
import Sheet from "./Sheet.svelte";
|
||||
import HalfSheet from "./HalfSheet.svelte";
|
||||
import Viewer from "./Viewer.svelte";
|
||||
import { heroArt } from "./heroart.svelte.js";
|
||||
import SyncRoom from "./SyncRoom.svelte";
|
||||
import Home from "./screens/Home.svelte";
|
||||
import Search from "./screens/Search.svelte";
|
||||
@@ -121,6 +122,10 @@
|
||||
reading. A pushed page has one because it carries the back chevron,
|
||||
and it is static — there and titled from the first frame. */
|
||||
const showBar = $derived(!root);
|
||||
/* A hero page paints its picture a second time behind the scroller
|
||||
(heroart.svelte.js): the mask cuts the page where it meets the clock,
|
||||
and that copy fills the cut back in — so the photograph passes under
|
||||
the status bar and the name does not. The mask itself never moves. */
|
||||
const playing = $derived(player.status === "playing" || player.status === "buffering");
|
||||
const played = $derived(player.duration ? Math.min(1, player.position / player.duration) : 0);
|
||||
|
||||
@@ -334,10 +339,18 @@
|
||||
|
||||
<div class="pane">
|
||||
<div class="viewport">
|
||||
{#if meta.hero && heroArt.src}
|
||||
<div class="hero-behind" aria-hidden="true" style:--hero-shift="{-scrolled}px">
|
||||
<i style:background-image={`url("${heroArt.src}")`}></i>
|
||||
</div>
|
||||
<div class="hero-wash" aria-hidden="true"></div>
|
||||
{/if}
|
||||
{#key nav.seq}
|
||||
<div
|
||||
class="screen tr-{nav.tr} stagger"
|
||||
class:under-bar={!root && !meta.hero && !meta.flush}
|
||||
class:has-chrome={!root && !meta.hero && !meta.flush}
|
||||
data-title={meta.title}
|
||||
data-kicker={meta.kicker && meta.kicker !== meta.title ? meta.kicker : ""}
|
||||
class:has-hero={meta.hero}
|
||||
bind:this={screenEl}
|
||||
onscroll={(e) => { scrolled = e.currentTarget.scrollTop; }}
|
||||
@@ -383,14 +396,11 @@
|
||||
{/key}
|
||||
</div>
|
||||
|
||||
<!-- the bar a pushed page carries: back, the title, and the page's ⋯.
|
||||
A tab page has none — see showBar. -->
|
||||
<!-- no bar on any screen. A pushed page floats its two controls over
|
||||
the page instead; the title is the page's own, in 27px type. -->
|
||||
{#if showBar}
|
||||
<div class="gbar">
|
||||
<button class="iconbtn" onclick={back} aria-label="Back"><Icon name="chevron-left" size={24} /></button>
|
||||
<span class="gb-t">{meta.title}{#if meta.kicker && meta.kicker !== meta.title}<small>{meta.kicker}</small>{/if}</span>
|
||||
<button class="iconbtn muted" onclick={() => openHalf("more")} aria-label="More"><Icon name="more" size={21} /></button>
|
||||
</div>
|
||||
<button class="fchrome back" onclick={back} aria-label="Back"><Icon name="chevron-left" size={24} /></button>
|
||||
<button class="fchrome more" onclick={() => openHalf("more")} aria-label="More"><Icon name="more" size={21} /></button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
Reference in new issue
Block a user