feat(series): add AJAX library switching and stacked-cascade CSS

Create web/src/series.ts with Alpine component implementing the same
switchLibrary pattern as the dashboard:
- Fetch /api/series on library change instead of full page reload
- Fade out/in transition with loading spinner
- Re-render series grid and pagination from JSON response
- Save selected library to localStorage

Import series.ts in main.ts.

Add stacked-cascade CSS to input.css for multi-cover series cards:
- Covers cascade from top-left to bottom-right with increasing z-index
- Front cover sits at bottom-right (highest z-index)
- Separate layout rules for 1-7 covers with rotation offsets
- Hover lift effect on series cards
This commit is contained in:
2026-05-08 20:27:27 -04:00
parent b83e319a34
commit c5cda015b3
4 changed files with 324 additions and 1 deletions
+168
View File
@@ -398,4 +398,172 @@
border-right: none;
border-left: 1px solid var(--border);
}
.stacked-covers {
position: relative;
width: 100%;
aspect-ratio: 2 / 3;
overflow: hidden;
}
.stacked-covers .cover-img {
position: absolute;
width: 70%;
height: 90%;
object-fit: cover;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}
.stacked-covers.cover-count-1 .cover-0 {
top: 5%;
left: 15%;
}
.stacked-covers.cover-count-2 .cover-0 {
top: 8%;
left: 5%;
transform: rotate(-6deg);
z-index: 1;
}
.stacked-covers.cover-count-2 .cover-1 {
top: 2%;
left: 25%;
transform: rotate(3deg);
z-index: 2;
}
.stacked-covers.cover-count-3 .cover-0 {
top: 12%;
left: 2%;
transform: rotate(-8deg);
z-index: 1;
}
.stacked-covers.cover-count-3 .cover-1 {
top: 6%;
left: 15%;
transform: rotate(-2deg);
z-index: 2;
}
.stacked-covers.cover-count-3 .cover-2 {
top: 0;
left: 30%;
transform: rotate(4deg);
z-index: 3;
}
.stacked-covers.cover-count-4 .cover-0 {
top: 14%;
left: 0;
width: 65%;
height: 85%;
transform: rotate(-10deg);
z-index: 1;
}
.stacked-covers.cover-count-4 .cover-1 {
top: 10%;
left: 12%;
width: 65%;
height: 85%;
transform: rotate(-4deg);
z-index: 2;
}
.stacked-covers.cover-count-4 .cover-2 {
top: 5%;
left: 22%;
width: 65%;
height: 85%;
transform: rotate(2deg);
z-index: 3;
}
.stacked-covers.cover-count-4 .cover-3 {
top: 0;
left: 32%;
width: 65%;
height: 85%;
transform: rotate(6deg);
z-index: 4;
}
.stacked-covers.cover-count-5 .cover-0,
.stacked-covers.cover-count-6 .cover-0,
.stacked-covers.cover-count-7 .cover-0 {
top: 16%;
left: -2%;
width: 62%;
height: 82%;
transform: rotate(-12deg);
z-index: 1;
}
.stacked-covers.cover-count-5 .cover-1,
.stacked-covers.cover-count-6 .cover-1,
.stacked-covers.cover-count-7 .cover-1 {
top: 12%;
left: 8%;
width: 62%;
height: 82%;
transform: rotate(-6deg);
z-index: 2;
}
.stacked-covers.cover-count-5 .cover-2,
.stacked-covers.cover-count-6 .cover-2,
.stacked-covers.cover-count-7 .cover-2 {
top: 8%;
left: 17%;
width: 62%;
height: 82%;
transform: rotate(-1deg);
z-index: 3;
}
.stacked-covers.cover-count-5 .cover-3,
.stacked-covers.cover-count-6 .cover-3,
.stacked-covers.cover-count-7 .cover-3 {
top: 4%;
left: 26%;
width: 62%;
height: 82%;
transform: rotate(4deg);
z-index: 4;
}
.stacked-covers.cover-count-5 .cover-4,
.stacked-covers.cover-count-6 .cover-4,
.stacked-covers.cover-count-7 .cover-4 {
top: 0;
left: 35%;
width: 62%;
height: 82%;
transform: rotate(9deg);
z-index: 5;
}
.stacked-covers.cover-count-6 .cover-5,
.stacked-covers.cover-count-7 .cover-5 {
top: -2%;
left: 30%;
width: 60%;
height: 80%;
transform: rotate(13deg);
z-index: 6;
}
.stacked-covers.cover-count-7 .cover-6 {
top: -4%;
left: 25%;
width: 58%;
height: 78%;
transform: rotate(16deg);
z-index: 7;
}
.series-card {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.series-card:hover {
transform: translateY(-4px);
}
}