mirror of
https://github.com/narrrl/pl8list-rewind.git
synced 2026-11-04 16:47:00 +01:00
Turn artist radio into a station sheet with reasons on each track
The radio button (r) opens a sheet with the artist, their tags and their closest neighbours instead of switching the feed at once; tapping a neighbour walks the artist graph. Starting jumps to the radio's first track, the top-bar chip shows the station, each radio card says why it is there, and an end card offers the way back when the radio runs dry. GET /local/artist feeds the sheet and starts crawling a new artist's neighbourhood (at most every 10 minutes per artist). Artist-radio tracks carry why: seed, related, or via the neighbour that leads to them.
This commit is contained in:
1 parent
e8c0f40128
commit
76cfbb12ac
13 files changed
+654
-42
No files matched your search
+142
-3
@@ -83,12 +83,25 @@ button {
|
||||
}
|
||||
#bar button[aria-expanded="true"] { background: rgba(255, 255, 255, 0.22); }
|
||||
#mode-chip {
|
||||
max-width: 12rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 14rem;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.45em;
|
||||
padding-left: 0.3em;
|
||||
white-space: nowrap;
|
||||
background: color-mix(in oklab, var(--accent) 40%, transparent);
|
||||
}
|
||||
#mode-chip .chip-label { overflow: hidden; text-overflow: ellipsis; }
|
||||
.chip-avatar {
|
||||
flex: none;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
border-radius: 50%;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-size: 0.8em;
|
||||
background: #0006 center / cover no-repeat;
|
||||
}
|
||||
|
||||
.panel {
|
||||
position: fixed;
|
||||
@@ -305,6 +318,15 @@ button.danger:not(.link) { background: var(--accent); border: none; color: #fff;
|
||||
overflow-wrap: anywhere;
|
||||
text-wrap: balance;
|
||||
}
|
||||
.why {
|
||||
margin: 0;
|
||||
min-height: 0;
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
color: color-mix(in oklab, var(--card-accent) 70%, #fff);
|
||||
}
|
||||
.why:empty { display: none; }
|
||||
.byline { display: flex; align-items: center; gap: 0.6rem; }
|
||||
.artist { color: var(--muted); text-decoration: none; }
|
||||
.artist:hover { text-decoration: underline; }
|
||||
@@ -473,6 +495,118 @@ button.danger:not(.link) { background: var(--accent); border: none; color: #fff;
|
||||
}
|
||||
#start button { font-size: 1.3rem; padding: 0.7em 1.6em; background: var(--accent); border: none; color: #fff; }
|
||||
|
||||
/* Radio sheet: a bottom sheet on phones, a centred box elsewhere. */
|
||||
#station {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 26;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 1rem;
|
||||
background: #000b;
|
||||
animation: fade 0.2s ease;
|
||||
}
|
||||
.station-box {
|
||||
position: relative;
|
||||
width: min(26rem, 100%);
|
||||
max-height: calc(100dvh - 2rem);
|
||||
overflow-y: auto;
|
||||
background: var(--panel);
|
||||
border: 1px solid #333;
|
||||
border-radius: var(--radius-l);
|
||||
padding: 1.4rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.9rem;
|
||||
animation: rise 0.35s var(--ease-out);
|
||||
}
|
||||
@keyframes rise { from { transform: translateY(2rem); opacity: 0; } }
|
||||
#station-back { align-self: flex-start; }
|
||||
#station-close { position: absolute; top: 0.8rem; right: 0.8rem; }
|
||||
.icon-btn { width: 2.2rem; height: 2.2rem; padding: 0; display: grid; place-items: center; }
|
||||
.icon-btn svg { width: 1rem; height: 1rem; }
|
||||
.station-head { display: flex; gap: 1rem; align-items: center; }
|
||||
.station-head h2 { margin: 0; font: 800 1.4rem/1.1 var(--font-display); overflow-wrap: anywhere; }
|
||||
.station-kicker { margin: 0 0 0.2rem; font-size: 0.7rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent); }
|
||||
#station-sub { margin: 0.3rem 0 0.4rem; color: var(--muted); font-size: 0.85rem; }
|
||||
#station-tags { justify-content: flex-start; }
|
||||
#station-tags span { background: rgba(255, 255, 255, 0.12); }
|
||||
.station-avatar {
|
||||
flex: none;
|
||||
width: 5.5rem;
|
||||
height: 5.5rem;
|
||||
border-radius: 50%;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: #222 center / cover no-repeat;
|
||||
box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 60%, transparent), 0 10px 30px #000a;
|
||||
}
|
||||
.station-avatar .initial { font-size: 2rem; }
|
||||
.station-avatar.small { width: 3.4rem; height: 3.4rem; box-shadow: none; }
|
||||
.station-avatar.small .initial { font-size: 1.2rem; }
|
||||
.station-box h3 { margin: 0; font-size: 0.8rem; font-weight: 600; color: var(--muted); }
|
||||
#station-neighbours {
|
||||
list-style: none;
|
||||
margin: 0 -1.4rem;
|
||||
padding: 0 1.4rem 0.3rem;
|
||||
display: flex;
|
||||
gap: 0.7rem;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
color: var(--muted);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
#station-neighbours li { animation: fade 0.5s ease; }
|
||||
#station-neighbours button {
|
||||
width: 4.2rem;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 0.3rem;
|
||||
font-size: 0.72rem;
|
||||
color: var(--fg);
|
||||
}
|
||||
#station-neighbours button > span:last-child { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
#station-neighbours button:hover .station-avatar { box-shadow: 0 0 0 2px var(--accent); }
|
||||
#station-exploring { margin: 0; color: var(--muted); font-size: 0.85rem; animation: breathe 1.4s ease-in-out infinite; }
|
||||
@keyframes breathe { 50% { opacity: 0.35; } }
|
||||
button.primary {
|
||||
background: var(--accent);
|
||||
border: none;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
padding: 0.7em 1.2em;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.4em;
|
||||
}
|
||||
button.primary svg { width: 1em; height: 1em; }
|
||||
|
||||
/* The end of an artist radio, over its last card. */
|
||||
#radio-end {
|
||||
position: fixed;
|
||||
z-index: 6;
|
||||
left: 50%;
|
||||
bottom: max(6rem, calc(env(safe-area-inset-bottom) + 5rem));
|
||||
transform: translateX(-50%);
|
||||
width: min(24rem, calc(100vw - 2rem));
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-m);
|
||||
background: var(--panel);
|
||||
border: 1px solid #333;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
animation: rise 0.35s var(--ease-out);
|
||||
}
|
||||
#radio-end p { flex-basis: 100%; margin: 0 0 0.3rem; color: var(--muted); font-size: 0.9rem; }
|
||||
|
||||
#help {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
@@ -553,6 +687,11 @@ button.danger:not(.link) { background: var(--accent); border: none; color: #fff;
|
||||
#help-btn { display: none; }
|
||||
#account-btn { max-width: 6.5rem; }
|
||||
.panel { left: 0.7rem; right: 0.7rem; }
|
||||
#station { place-items: end stretch; padding: 0; }
|
||||
.station-box { width: 100%; border-radius: var(--radius-l) var(--radius-l) 0 0; padding-bottom: max(1.4rem, env(safe-area-inset-bottom)); }
|
||||
#mode-chip { max-width: 9rem; }
|
||||
/* A station chip says more than the logo; there is no room for both. */
|
||||
#bar:has(#mode-chip:not([hidden])) .logo { display: none; }
|
||||
#likes { width: auto; }
|
||||
.content { width: calc(100vw - 2rem); margin-bottom: 4.5rem; }
|
||||
.actions {
|
||||
|
||||
Reference in new issue
Block a user