library: the page lit by its own records, and portraits that prove out
The expander reserves once on the page root instead of standing min-heights on the grid; shelf heads carry artist portraits keyed by path; every record tints its own card, expander and glow from its cover; the artwork opens the shared viewer; the banner wash reads the playing record and actually paints (isolation, the artist page's own fix); descriptions animate on both pages via partAnim. Portrait resolution rejects placeholder rows and never stores a silhouette (pick_artist_portrait, 3 tests); repair re-resolves the missing.
This commit is contained in:
1 parent
741c4ae6dc
commit
185c29fc33
5 files changed
+1068
-49
No files matched your search
@@ -3816,6 +3816,39 @@
|
||||
};
|
||||
}
|
||||
|
||||
/** Fade+lift ONE element from its own mount, with the same WAAPI call
|
||||
* `staggerIn` makes.
|
||||
*
|
||||
* The description needs this because `in:partIn` above never actually ran
|
||||
* for it, and that was measured on this page: opening a Weezer record and
|
||||
* sampling `.rail-lede` every frame gave opacity 1 on all 1389 frames cold
|
||||
* and all 711 warm, with no transform at any point.
|
||||
*
|
||||
* Both of the other mechanisms miss it by construction. `staggerIn` is an
|
||||
* action on .xp-panel and queries for its parts when that element is
|
||||
* created, but the description block is created microseconds later — same
|
||||
* flush — so the query finds nothing, which is why .rail-desc was left out
|
||||
* of SWAP_PARTS in the first place. And Svelte plays an `in:` only for an
|
||||
* element added by a later UPDATE; landing in the same flush is not one, so
|
||||
* the intro is skipped too. An action always runs when its own element is
|
||||
* created, whichever flush that is, so it covers every arrival time. */
|
||||
function partAnim(node, { delay = 0 } = {}) {
|
||||
if (reducedMotion()) return;
|
||||
const rest = getComputedStyle(node).opacity || "1";
|
||||
node.animate(
|
||||
[
|
||||
{ opacity: 0, transform: "translateY(7px)" },
|
||||
{ opacity: rest, transform: "none" },
|
||||
],
|
||||
{
|
||||
duration: SWAP_MS * 0.7,
|
||||
delay: delay * (SWAP_MS / 360),
|
||||
easing: SWAP_EASE,
|
||||
fill: "backwards",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** Hold the stage at an eased height that TRACKS the panel, rather than
|
||||
* animating once to a height measured up front. The panel is at its
|
||||
* smallest when the swap begins — no description, no track rows — so a
|
||||
@@ -5269,19 +5302,19 @@
|
||||
keeps its 4-line clamp and
|
||||
its mask fade. -->
|
||||
{#if railDesc.lede}
|
||||
<p class="rail-lede" in:partIn={{ delay: 55 }}>{railDesc.lede}</p>
|
||||
<p class="rail-lede" use:partAnim={{ delay: 55 }}>{railDesc.lede}</p>
|
||||
{/if}
|
||||
{#if railDesc.rest}
|
||||
<p class="rail-desc" class:clamped={!descExpanded} in:partIn={{ delay: 60 }}>{railDesc.rest}</p>
|
||||
<p class="rail-desc" class:clamped={!descExpanded} use:partAnim={{ delay: 60 }}>{railDesc.rest}</p>
|
||||
<button
|
||||
class="rail-more"
|
||||
class:open={descExpanded}
|
||||
in:partIn={{ delay: 85 }}
|
||||
use:partAnim={{ delay: 85 }}
|
||||
onclick={() => (descExpanded = !descExpanded)}
|
||||
>{descExpanded ? "Show less" : "Show more"} <Icon name="chevron-down" size={13} /></button>
|
||||
{/if}
|
||||
{#if openInfo.genre || openInfo.label}
|
||||
<div class="rail-pills" in:partIn={{ delay: 100 }}>
|
||||
<div class="rail-pills" use:partAnim={{ delay: 100 }}>
|
||||
{#if openInfo.genre}<span class="gpill accent">{openInfo.genre}</span>{/if}
|
||||
{#if openInfo.label}<span class="gpill">{openInfo.label}</span>{/if}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user