mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 06:25:41 +00:00
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:
+92
-217
@@ -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);
|
||||
|
||||
@@ -422,6 +422,61 @@
|
||||
--danger: #bf616a;
|
||||
}
|
||||
|
||||
/* ─── Psychowave — Synthwave / Retrowave ─── */
|
||||
[data-theme='psychowave'] {
|
||||
color-scheme: dark;
|
||||
--select-arrow: url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22%23c9a8ff%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cpolyline%20points%3D%226%209%2012%2015%2018%209%22%3E%3C%2Fpolyline%3E%3C%2Fsvg%3E");
|
||||
|
||||
/* ── Palette ── */
|
||||
--ctp-crust: #120720;
|
||||
--ctp-mantle: #160a28;
|
||||
--ctp-base: #1c0b35;
|
||||
--ctp-surface0: #2a1050;
|
||||
--ctp-surface1: #381860;
|
||||
--ctp-surface2: #4a2878;
|
||||
--ctp-overlay0: #5e3d90;
|
||||
--ctp-overlay1: #7b50b8;
|
||||
--ctp-overlay2: #9868d8;
|
||||
/* Soft lavender text */
|
||||
--ctp-text: #f0e6ff;
|
||||
--ctp-subtext1: #c9a8ff;
|
||||
--ctp-subtext0: #a882e0;
|
||||
/* Neon accents */
|
||||
--ctp-mauve: #ff2d78; /* hot pink — primary accent & button gradient start */
|
||||
--ctp-lavender: #bf5fff; /* electric purple — button gradient end */
|
||||
--ctp-pink: #ff2d78;
|
||||
--ctp-flamingo: #ff6b9d;
|
||||
--ctp-rosewater: #ff8fc0;
|
||||
--ctp-blue: #00d4ff; /* electric cyan */
|
||||
--ctp-sapphire: #007aff;
|
||||
--ctp-sky: #00bfff;
|
||||
--ctp-teal: #00e5cc;
|
||||
--ctp-green: #39ff14; /* neon green */
|
||||
--ctp-yellow: #ffd700; /* neon gold */
|
||||
--ctp-peach: #ff8c42;
|
||||
--ctp-maroon: #ff4d6d;
|
||||
--ctp-red: #ff2d55;
|
||||
|
||||
/* ── Semantic tokens ── */
|
||||
--bg-app: #1c0b35;
|
||||
--bg-sidebar: #160a28;
|
||||
--bg-card: #2a1050;
|
||||
--bg-hover: #381860;
|
||||
--bg-player: #120720;
|
||||
--bg-glass: rgba(28, 11, 53, 0.82);
|
||||
--accent: #ff2d78;
|
||||
--accent-dim: rgba(255, 45, 120, 0.15);
|
||||
--accent-glow: rgba(255, 45, 120, 0.45);
|
||||
--text-primary: #f0e6ff;
|
||||
--text-secondary:#c9a8ff;
|
||||
--text-muted: #9b72d0;
|
||||
--border: #4a2878;
|
||||
--border-subtle: #2a1050;
|
||||
--positive: #39ff14;
|
||||
--warning: #ffd700;
|
||||
--danger: #ff2d55;
|
||||
}
|
||||
|
||||
/* ─── Global Base Settings ─── */
|
||||
:root {
|
||||
/* Typography */
|
||||
|
||||
Reference in New Issue
Block a user