forked from Keelrleek/SoulStream
one listener bar, five surfaces: the search page's pill wins and the heat wash is retired
Kyle asked for the popularity bars to match the search page "to keep a
consistent design across the board". They were two designs, and one of them had
quietly drifted into a third.
THE SEARCH PAGE'S PILL IS NOW THE APP-WIDE DESIGN: a visible
rgba(255,255,255,0.07) track, a solid var(--accent) fill scaled by the shared
curve, the full comma-formatted count standing centred on top, 17px tall with
a 4px radius. The album page, the artist page's open-in-place album panel, the
discography's record sections and the artist page's Popular list all draw it.
WHAT WENT AWAY IS THE HEAT WASH. Those three surfaces drove the fill's COLOUR
and ALPHA from the count (--t on the row, alpha ramping 0.30-0.85 via a double
color-mix, digits ramping with it), so length and colour told one story twice
and every page read as a different design. Length is the only encoding now, and
listenerHeat is deleted from listenerBars.js — nothing computes a temperature
any more. The dark-glass CONTRACT's deliberate exclusion for these bars ("their
alpha IS the data encoding") is moot and marked so.
THE DISCOGRAPHY HAD DRIFTED FURTHEST and is the reason this mattered: it
computed peak/heat inline with a LINEAR share, a 6% floor and abbreviated
labels ("12K"), against everyone else's COUNT_GAMMA 0.42 curve, browser-measured
max-content floor and full figures. It now imports listenerBars.js like the
rest, so no two surfaces can disagree about how big a hit is — the reason the
module exists.
THE POPULAR LIST joins them: it was plain right-aligned muted text, deliberately
not accent so the row's own `.now` accent stayed distinct (the ♫ rank and title
still carry that). Its column widened 62px -> 96px, the pill's width, and its
peak is taken over the WHOLE list rather than the visible five so the bars do
not re-scale on Show more.
Kept throughout: unknown counts render an em dash with NO bar (a floor-width
pill behind a dash reads as a measured tiny value), and a column drops entirely
when Last.fm knew nothing.
Verified live against the running app — computed styles on the album and artist
pages match the search bar value-for-value. 98/98 tests.
This commit is contained in:
1 parent
385b598170
commit
73a677d35c
4 files changed
+131
-137
No files matched your search
@@ -23,6 +23,7 @@
|
||||
import { editionLabel } from "../lib/editions.js";
|
||||
import { accentFromImage } from "../lib/color.js";
|
||||
import { catalogAlbum } from "../lib/api.js";
|
||||
import { listenerPeak, listenerShare } from "../lib/listenerBars.js";
|
||||
|
||||
/* ---------------------------------------------------------- the releases
|
||||
* NOT re-filed here. `catalog.discoTabs` is the artist page's OWN computed
|
||||
@@ -379,6 +380,9 @@
|
||||
: n >= 1000
|
||||
? Math.round(n / 1000) + "K"
|
||||
: String(n);
|
||||
/** The listener bar prints the full figure like every other tracklist —
|
||||
the abbreviated fmtCount above stays for the fans chip. */
|
||||
const fmtCountFull = (n) => (n == null ? "—" : n.toLocaleString());
|
||||
const fmtDur = (s) =>
|
||||
s == null ? "—" : Math.floor(s / 60) + ":" + String(s % 60).padStart(2, "0");
|
||||
const runtimeOf = (rows) => {
|
||||
@@ -607,7 +611,7 @@
|
||||
{/if}
|
||||
|
||||
{#if rows?.length}
|
||||
{@const peak = Math.max(1, ...rows.map((x) => x.listeners || 0))}
|
||||
{@const peak = listenerPeak(rows)}
|
||||
{@const hasCount = rows.some((x) => x.listeners != null)}
|
||||
<div class="tracklist">
|
||||
<div class="tcols" class:has-count={hasCount}>
|
||||
@@ -616,18 +620,19 @@
|
||||
<span class="tdur-head"><Icon name="clock" size={13} /></span>
|
||||
</div>
|
||||
{#each rows as t (t.id)}
|
||||
{@const heat = (t.listeners || 0) / peak}
|
||||
<div class="trow" class:has-count={hasCount} style="--t:{heat.toFixed(3)}">
|
||||
<div class="trow" class:has-count={hasCount}>
|
||||
<span class="tno">{t.track_no ?? "·"}</span>
|
||||
<span class="ttitle">{t.title}</span>
|
||||
{#if hasCount}
|
||||
<span class="tcount" class:empty={!t.listeners}>
|
||||
<span
|
||||
class="tcount-fill"
|
||||
style="width:{Math.max(6, heat * 100).toFixed(1)}%"
|
||||
>
|
||||
<span class="tcount-val">{fmtCount(t.listeners)}</span>
|
||||
</span>
|
||||
<span
|
||||
class="tcount"
|
||||
class:empty={!t.listeners}
|
||||
title={t.listeners == null
|
||||
? "No Last.fm listener count for this track"
|
||||
: `${t.listeners.toLocaleString()} Last.fm listeners`}
|
||||
>
|
||||
<i style="transform:scaleX({listenerShare(t.listeners, peak) / 100})"></i>
|
||||
<span class="tcount-val">{fmtCountFull(t.listeners)}</span>
|
||||
</span>
|
||||
{/if}
|
||||
<span class="tdur">{fmtDur(t.duration_secs)}</span>
|
||||
@@ -1009,37 +1014,44 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
/* Listener weight, the app-wide design: the search page's pill — a visible
|
||||
track, a solid accent fill scaled by the shared curve, the number
|
||||
standing centred on top (src/lib/listenerBars.js). Length is the ONLY
|
||||
encoding: the old heat wash here drove colour AND alpha from the count
|
||||
(--t on the row), which made every page's bars read as a different
|
||||
design — retired for the one pill (Kyle, 2026-08-30). Keep these rules
|
||||
in step with .lbar in CatalogSearch.svelte — one dial, four files.
|
||||
align-self centres the 17px pill in the taller grid row. */
|
||||
.rec .tcount {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 21px;
|
||||
height: 17px;
|
||||
align-self: center;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
font-size: 12.5px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
background: rgba(255, 255, 255, 0.07);
|
||||
}
|
||||
.rec .tcount.empty .tcount-fill {
|
||||
background: none;
|
||||
}
|
||||
.rec .tcount-fill {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
min-width: max-content;
|
||||
max-width: 100%;
|
||||
.rec .tcount i {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 4px;
|
||||
background: color-mix(
|
||||
in srgb,
|
||||
color-mix(in srgb, var(--accent-hover) calc(22% + var(--t) * 78%), var(--text-muted))
|
||||
calc(30% + var(--t) * 55%),
|
||||
transparent
|
||||
);
|
||||
background: var(--accent);
|
||||
transform-origin: left;
|
||||
transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
/* A track Last.fm has no count for gets the dash and no paint at all — a
|
||||
floor-width bar behind an em dash would read as a measured tiny value. */
|
||||
.rec .tcount.empty i {
|
||||
display: none;
|
||||
}
|
||||
.rec .tcount-val {
|
||||
padding: 0 8px;
|
||||
white-space: nowrap;
|
||||
color: color-mix(in srgb, #fff calc(45% + var(--t) * 55%), var(--text-muted));
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-size: 10.5px;
|
||||
font-weight: 800;
|
||||
font-variant-numeric: tabular-nums;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
.rec .tdur {
|
||||
text-align: right;
|
||||
|
||||
Reference in new issue
Block a user