feat: Last.fm beta, Similar Artists, Statistics Last.fm stats, TooltipPortal, CustomSelect, Psychowave theme (v1.7.0)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Psychotoxical
2026-03-20 18:35:26 +01:00
parent 9400a5fb2b
commit 2ba7845c79
31 changed files with 1379 additions and 365 deletions
+92 -217
View File
@@ -1716,57 +1716,6 @@
}
}
/* ── Drifting color orbs ── */
@keyframes orb-a {
0% {
transform: translate(0px, 0px) scale(1);
}
33% {
transform: translate(90px, -50px) scale(1.12);
}
66% {
transform: translate(-40px, 70px) scale(0.94);
}
100% {
transform: translate(0px, 0px) scale(1);
}
}
@keyframes orb-b {
0% {
transform: translate(0px, 0px) scale(1);
}
33% {
transform: translate(-70px, 40px) scale(1.08);
}
66% {
transform: translate(50px, -60px) scale(1.14);
}
100% {
transform: translate(0px, 0px) scale(1);
}
}
@keyframes orb-c {
0% {
transform: translate(0px, 0px) scale(1);
}
50% {
transform: translate(60px, 50px) scale(0.9);
}
100% {
transform: translate(0px, 0px) scale(1);
}
}
/* ── Cover breathing ── */
@keyframes cover-breathe {
@@ -1781,38 +1730,23 @@
}
@keyframes ken-burns {
0% {
transform: scale(1.08) translate(0%, 0%);
}
25% {
transform: scale(1.12) translate(-1.5%, 1%);
}
50% {
transform: scale(1.10) translate(1%, -1.5%);
}
75% {
transform: scale(1.13) translate(1.5%, 0.5%);
}
100% {
transform: scale(1.08) translate(0%, 0%);
}
0% { transform: scale(1.1) translate(-8%, -8%); }
25% { transform: scale(1.15) translate(8%, -8%); }
50% { transform: scale(1.1) translate(8%, 8%); }
75% { transform: scale(1.15) translate(-8%, 8%); }
100% { transform: scale(1.1) translate(-8%, -8%); }
}
/* ── Blurred background ── */
.fs-bg {
position: absolute;
inset: -15%;
inset: -30%;
background-size: cover;
background-position: top center;
background-position: center center;
filter: blur(6px) brightness(0.25) saturate(1.6);
animation: ken-burns 40s ease-in-out infinite;
transform: scale(1.2);
animation: ken-burns 90s ease-in-out infinite;
z-index: 0;
will-change: opacity;
will-change: transform;
pointer-events: none;
transition: opacity 700ms ease;
}
@@ -1825,45 +1759,6 @@
pointer-events: none;
}
/* ── Drifting color orbs ── */
.fs-orb {
position: absolute;
border-radius: 50%;
filter: blur(110px);
opacity: 0.22;
pointer-events: none;
z-index: 0;
}
.fs-orb-1 {
background: var(--ctp-mauve);
width: 700px;
height: 700px;
top: -220px;
left: -180px;
animation: orb-a 20s ease-in-out infinite;
}
.fs-orb-2 {
background: var(--ctp-blue);
width: 600px;
height: 600px;
bottom: -180px;
right: -120px;
animation: orb-b 26s ease-in-out infinite;
animation-delay: -9s;
}
.fs-orb-3 {
background: var(--ctp-lavender);
width: 480px;
height: 480px;
top: 35%;
right: 5%;
animation: orb-c 17s ease-in-out infinite;
animation-delay: -14s;
}
/* ── Close button ── */
.fs-close {
position: absolute;
@@ -2272,71 +2167,11 @@
}
/* ─ CSS Tooltips ─ */
/* Tooltips are handled by TooltipPortal (React portal) — no CSS pseudo-elements needed */
[data-tooltip] {
position: relative;
}
[data-tooltip]::before,
[data-tooltip]::after {
position: absolute;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, visibility 0.2s ease;
pointer-events: none;
z-index: 9999;
}
[data-tooltip]::before {
content: '';
border: 5px solid transparent;
border-top-color: var(--border-subtle);
bottom: 100%;
left: 50%;
transform: translateX(-50%);
}
/* Inner triangle for the tooltip arrow to match background */
[data-tooltip]::after {
content: attr(data-tooltip);
background: var(--bg-card);
color: var(--text-primary);
padding: var(--space-1) var(--space-2);
border-radius: var(--radius-sm);
font-size: 12px;
bottom: calc(100% + 6px);
left: 50%;
transform: translateX(-50%);
white-space: nowrap;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.6);
font-weight: 500;
border: 1px solid var(--border-subtle);
}
[data-tooltip]:hover::before,
[data-tooltip]:hover::after {
opacity: 1;
visibility: visible;
}
[data-tooltip-wrap]::after {
white-space: pre-line;
max-width: 220px;
text-align: left;
}
/* Modifiers for position */
[data-tooltip][data-tooltip-pos="bottom"]::before {
top: 100%;
bottom: auto;
border-top-color: transparent;
border-bottom-color: var(--border-subtle);
}
[data-tooltip][data-tooltip-pos="bottom"]::after {
top: calc(100% + 6px);
bottom: auto;
}
/* ─ Playlists Page ─ */
.playlist-page-header {
@@ -2479,7 +2314,7 @@
.stats-overview {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-columns: repeat(3, 1fr);
gap: var(--space-4);
}
@@ -2746,47 +2581,6 @@
background: linear-gradient(to bottom, transparent 40%, var(--bg-app) 100%);
}
/* Orbs — will-change promotes each to its own compositor layer so
the blur is composited on the GPU and doesn't repaint on every frame */
.np-orb {
position: absolute;
border-radius: 50%;
filter: blur(70px);
opacity: 0.3;
pointer-events: none;
z-index: 0;
will-change: transform;
}
.np-orb-1 {
background: var(--ctp-mauve);
width: 400px;
height: 400px;
top: -120px;
left: -80px;
animation: orb-a 18s ease-in-out infinite;
}
.np-orb-2 {
background: var(--ctp-blue);
width: 320px;
height: 320px;
bottom: -80px;
right: -60px;
animation: orb-b 24s ease-in-out infinite;
animation-delay: -9s;
}
.np-orb-3 {
background: var(--ctp-lavender);
width: 260px;
height: 260px;
top: 30%;
right: 10%;
animation: orb-c 15s ease-in-out infinite;
animation-delay: -5s;
}
.np-hero-content {
position: relative;
z-index: 1;
@@ -3208,6 +3002,87 @@
min-width: 140px;
}
/* ─ Custom Select ─ */
.custom-select-trigger {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
width: 100%;
padding: 0.5rem 0.75rem;
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
color: var(--text-primary);
font-size: 0.875rem;
cursor: pointer;
text-align: left;
transition: border-color 0.15s, background 0.15s;
}
.custom-select-trigger:hover {
border-color: var(--accent);
background: var(--bg-hover);
}
.custom-select-trigger:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
.custom-select-label {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.custom-select-chevron {
flex-shrink: 0;
color: var(--text-muted);
transition: transform 0.2s ease;
}
.custom-select-chevron.open {
transform: rotate(180deg);
}
.custom-select-dropdown {
background: var(--bg-card);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
overflow-y: auto;
overscroll-behavior: contain;
}
.custom-select-option {
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
color: var(--text-primary);
cursor: pointer;
transition: background 0.1s;
}
.custom-select-option:hover {
background: var(--bg-hover);
}
.custom-select-option.selected {
color: var(--accent);
font-weight: 600;
background: color-mix(in srgb, var(--accent) 8%, transparent);
}
.custom-select-option.disabled {
color: var(--text-muted);
cursor: default;
}
.custom-select-group-label {
padding: 0.375rem 0.75rem 0.25rem;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--text-muted);
border-top: 1px solid var(--border-subtle);
margin-top: 0.25rem;
}
.custom-select-group-label:first-child {
border-top: none;
margin-top: 0;
}
.eq-ctrl-btn {
background: var(--bg-hover);
border: 1px solid var(--border);