Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,15 @@ module.exports = withBundleAnalyzer({
name: 'preset-default',
params: { overrides: { removeViewBox: false } },
},
// preset-default's cleanupIds minifies internal ids to short
// strings (a, b, c…) per file. SVGR inlines every icon into the
// same DOM, so icons that reference their own clipPath/filter/
// gradient via url(#id) (book, video, their shadows, delete,
// edit) end up with colliding ids — url(#a) resolves to whichever
// #a renders first, pointing at the wrong def and rendering blank.
// prefixIds namespaces each file's ids by filename so they stay
// unique across icons.
'prefixIds',
],
},
},
Expand Down
Binary file added public/assets/library/audio-cover.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/assets/library/book-cover.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 29 additions & 7 deletions src/components/library/molecules/AudioCard/AudioCard.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,29 @@
opacity: 0.55;
}

// The vinyl mockup body (sleeve + disc edge baked in). Always rendered behind
// the cover so an uncovered object reads as a blank sleeve and a covered one
// keeps the disc peeking out on the right.
.placeholder {
position: absolute;
top: 0;
left: 0;
width: 230px;
height: 194px;
z-index: 0;
background: url('/assets/library/audio-cover.png') center / cover no-repeat;
}

// The uploaded album art sits on the square sleeve only — 190×190, flush to the
// right edge so the placeholder's disc edge stays exposed on the left.
.cover {
position: absolute;
inset: 0;
top: 0;
right: 0;
width: 190px;
height: 190px;
overflow: hidden;
z-index: 1;
transition: opacity 0.15s ease;
}

Expand All @@ -66,12 +85,6 @@
object-fit: cover;
}

.coverPlaceholder {
width: 100%;
height: 100%;
background: var(--white);
}

.tags {
display: flex;
flex-direction: column;
Expand All @@ -94,6 +107,15 @@
height: 138px;
gap: 0;

.placeholder,
.cover {
inset: 0;
top: 0;
left: 0;
width: 100%;
height: 100%;
}

.tags {
display: none;
}
Expand Down
5 changes: 2 additions & 3 deletions src/components/library/molecules/AudioCard/AudioCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,16 @@ export function AudioCard({
/>
</div>
)}
<div className={styles.placeholder} aria-hidden="true" />
<div className={styles.cover}>
{coverUrl ? (
{coverUrl && (
<Image
src={coverUrl}
alt={title}
fill
sizes="190px"
className={styles.coverImage}
/>
) : (
<div className={styles.coverPlaceholder} />
)}
</div>
</div>
Expand Down
66 changes: 25 additions & 41 deletions src/components/library/molecules/BookCard/BookCard.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
.select {
position: absolute;
top: 8px;
left: 33px;
width: 146px;
left: 0;
width: 100%;
display: flex;
justify-content: center;
z-index: 2;
Expand All @@ -51,20 +51,30 @@
opacity: 0.55;
}

// The book body: a complete 3D mockup (spine + binding + drop shadow baked in).
// Always rendered behind the cover so an uncovered book reads as a blank book
// and a covered one keeps its spine and shadow. Spans the full 180×208 card.
.placeholder {
position: absolute;
bottom: -6px;
left: -5px;
width: 180px;
height: 208px;
z-index: 0;
background: url('/assets/library/book-cover.png') center / cover no-repeat;
}

// The uploaded cover art sits on the book's front face only — 146×206, flush to
// the right edge so the placeholder's 34px spine stays exposed on the left.
.cover {
position: absolute;
top: 0;
left: 33px;
top: 1px;
right: 0;
width: 146px;
height: 206px;
border-radius: 1.44px;
overflow: hidden;
z-index: 1;
transition: opacity 0.15s ease;
// Paint the gradient straight onto the cover box so the card shows a filled
// spine immediately — both for cover-less objects and while next/image is
// still fetching/optimizing a freshly uploaded cover (which otherwise left a
// blank gap on add).
background: var(--gradient-book-placeholder);
}

.coverImage {
Expand All @@ -74,28 +84,6 @@
object-fit: cover;
}

.coverPlaceholder {
width: 100%;
height: 100%;
background: var(--gradient-book-placeholder);
}

.alpha {
position: absolute;
inset: 0;
background: var(--surface-overlay);
pointer-events: none;
}

.shadow {
position: absolute;
top: 0;
left: 0;
width: 180px;
height: 208px;
pointer-events: none;
}

.tags {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -123,19 +111,15 @@
}

.cover {
left: 22px;
top: 1px;
right: 0;
width: 97px;
height: 138px;
}

.shadow {
width: 119px;
height: 138px;
height: 137px;
}

.select {
left: 22px;
width: 97px;
left: 0;
width: 100%;
}

.tags {
Expand Down
9 changes: 2 additions & 7 deletions src/components/library/molecules/BookCard/BookCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import classNames from 'classnames';
import Image from 'next/image';
import React, { JSX } from 'react';

import { BookShadowIcon } from '@icons/library/svg';

import { SelectToggle } from '@components/library/molecules/SelectToggle';

import type { BookCardProps } from './BookCard.types';
Expand Down Expand Up @@ -59,21 +57,18 @@ export function BookCard({
/>
</div>
)}
<div className={styles.placeholder} aria-hidden="true" />
<div className={styles.cover}>
{coverUrl ? (
{coverUrl && (
<Image
src={coverUrl}
alt={attributes.title}
fill
sizes="146px"
className={styles.coverImage}
/>
) : (
<div className={styles.coverPlaceholder} />
)}
<div className={styles.alpha} aria-hidden />
</div>
<BookShadowIcon className={styles.shadow} aria-hidden />
</div>

{/* Always render the tag column (even when empty) so the card keeps a
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.wrapper {
position: relative;
display: grid;
grid-template-columns: auto 1fr auto;
align-items: start;
Expand All @@ -23,8 +24,9 @@
}

.circle {
width: 28px;
height: 28px;
box-sizing: border-box;
width: 36px;
height: 36px;
border-radius: 50%;
display: flex;
align-items: center;
Expand All @@ -35,6 +37,7 @@
transition:
background 0.3s ease,
border-color 0.3s ease,
box-shadow 0.3s ease,
color 0.3s ease;
}

Expand All @@ -59,10 +62,13 @@
transition: color 0.3s ease;
}

// Active/completed: a green disc with a white inner gap and a green outer
// ring — the white 3px border carves the gap, the box-shadow draws the ring.
&.active {
.circle {
background: var(--green-200);
border-color: var(--green-200);
border: 3px solid var(--white);
box-shadow: 0 0 0 2px var(--green-200);
color: var(--white);
}

Expand All @@ -74,7 +80,8 @@
&.completed {
.circle {
background: var(--green-200);
border-color: var(--green-200);
border: 3px solid var(--white);
box-shadow: 0 0 0 2px var(--green-200);
color: var(--white);

svg path {
Expand All @@ -88,12 +95,17 @@
}
}

// Span the line circle-to-circle, not column-to-column: anchored to the
// wrapper so the wide step labels can't squeeze it. 46px = 36px circle + a
// 10px gap on each side; top 18px centers it on the circle (radius 18px).
.connector {
position: absolute;
top: 18px;
left: 46px;
right: 46px;
height: 1px;
background: var(--gray-100);
position: relative;
overflow: hidden;
margin-top: 14px;
}

.connectorFill {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
.wrapper {
position: relative;
width: 100%;
}

// Anchors the dropdown menu to the trigger alone — kept separate from the
// selected chips below so the chips' height never pushes the menu off the box.
.field {
position: relative;
}

.trigger {
width: 100%;
display: flex;
Expand Down
Loading
Loading