mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
Refactor "Similar Content" UI for modern layout and responsiveness
- Refactored `SimilarContentCard.js` to use Flexbox layout. - Updated `css/tailwind.source.css` to remove grid layout and use standard flex utilities. - Improved thumbnail sizing (16:9, fixed width) and responsiveness. - Added hover effects (background color, thumbnail zoom). - Cleaned up typography and metadata hierarchy.
This commit is contained in:
1 parent
5e545e91dd
commit
90adb7db21
2 files changed
+90
-171
No files matched your search
+27
-138
@@ -2085,82 +2085,24 @@
|
||||
}
|
||||
|
||||
.player-modal__similar-card {
|
||||
--player-modal-similar-thumb: clamp(
|
||||
11rem,
|
||||
calc(var(--video-modal-secondary-min, 320px) * 0.62),
|
||||
17rem
|
||||
);
|
||||
--player-modal-similar-gap: clamp(var(--space-4xs), 0.65vw, var(--space-2xs));
|
||||
--player-modal-similar-padding: clamp(
|
||||
var(--space-4xs),
|
||||
0.7vw,
|
||||
var(--space-sm)
|
||||
);
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: var(--player-modal-similar-thumb) minmax(0, 1fr);
|
||||
align-items: stretch;
|
||||
gap: var(--player-modal-similar-gap);
|
||||
padding: var(--player-modal-similar-padding);
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
transform: translate3d(0, var(--video-card-lift-translate-y, 0), 0);
|
||||
}
|
||||
|
||||
.player-modal__similar-card::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
opacity: var(--video-card-backdrop-opacity, 0);
|
||||
background-image: var(
|
||||
--similar-card-thumb-url,
|
||||
var(--video-card-thumb-url, none)
|
||||
);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
transform: translate3d(
|
||||
0,
|
||||
var(--video-card-backdrop-translate-y, 0),
|
||||
0
|
||||
)
|
||||
scale(var(--video-card-backdrop-scale, 1));
|
||||
filter: blur(var(--video-card-backdrop-blur, 0));
|
||||
transition:
|
||||
opacity var(--motion-duration-base) var(--motion-ease-in-out),
|
||||
transform var(--motion-duration-base) var(--motion-ease-in-out),
|
||||
filter var(--motion-duration-base) var(--motion-ease-in-out);
|
||||
pointer-events: none;
|
||||
will-change: opacity, transform, filter;
|
||||
}
|
||||
|
||||
.player-modal__similar-card > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
@apply focus-ring relative flex w-full min-w-0 items-start gap-3 rounded-xl p-2 transition duration-base ease-out;
|
||||
}
|
||||
|
||||
.player-modal__similar-card:hover,
|
||||
.player-modal__similar-card:focus-within {
|
||||
--video-card-backdrop-opacity: 0.425;
|
||||
--video-card-backdrop-translate-y: 0.15rem;
|
||||
--video-card-backdrop-scale: 1.12;
|
||||
--video-card-backdrop-blur: 24px;
|
||||
background-color: var(--surface-panel-hover);
|
||||
}
|
||||
|
||||
.player-modal__similar-card-media {
|
||||
display: block;
|
||||
position: relative;
|
||||
inline-size: var(--player-modal-similar-thumb);
|
||||
block-size: 100%;
|
||||
max-inline-size: clamp(
|
||||
calc(var(--space-2xl-plus) * 1.1),
|
||||
28vw,
|
||||
calc(var(--space-2xl-plus) * 2.1)
|
||||
);
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
isolation: isolate;
|
||||
@apply relative block shrink-0 overflow-hidden rounded-lg bg-surface-alt;
|
||||
width: 168px;
|
||||
aspect-ratio: 16 / 9;
|
||||
}
|
||||
|
||||
@media (max-width: theme("screens.sm")) {
|
||||
.player-modal__similar-card-media {
|
||||
width: 140px;
|
||||
}
|
||||
}
|
||||
|
||||
.player-modal__similar-card-media img {
|
||||
@@ -2172,97 +2114,45 @@
|
||||
}
|
||||
|
||||
.player-modal__similar-card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: clamp(var(--space-4xs), 0.6vw, var(--space-2xs));
|
||||
min-width: 0;
|
||||
@apply flex min-w-0 flex-1 flex-col gap-1;
|
||||
}
|
||||
|
||||
.player-modal__similar-card-title {
|
||||
@apply text-sm font-semibold leading-snug text-text transition-colors duration-base ease-out;
|
||||
display: -webkit-box;
|
||||
margin: 0;
|
||||
font-size: clamp(var(--font-size-sm), 1.05vw, var(--font-size-base));
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
color: var(--color-text);
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
transition: color var(--motion-duration-base) var(--motion-ease-in-out);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.player-modal__similar-card-title:hover,
|
||||
.player-modal__similar-card:hover .player-modal__similar-card-title,
|
||||
.player-modal__similar-card-title:focus-visible {
|
||||
color: var(--color-accent);
|
||||
color: var(--color-accent-strong);
|
||||
}
|
||||
|
||||
.player-modal__similar-card-author {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: clamp(var(--space-5xs), 0.6vw, var(--space-3xs));
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-text-muted);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.player-modal__similar-card-avatar {
|
||||
flex: 0 0 auto;
|
||||
display: block;
|
||||
inline-size: clamp(2.5rem, 0.8vw + 2.1rem, 3rem);
|
||||
block-size: clamp(2.5rem, 0.8vw + 2.1rem, 3rem);
|
||||
border-radius: var(--radius-full);
|
||||
overflow: hidden;
|
||||
background-color: var(--color-surface-alt);
|
||||
box-shadow: 0 0 0 1px
|
||||
color-mix(in srgb, var(--color-border-strong) 65%, transparent);
|
||||
}
|
||||
|
||||
.player-modal__similar-card-avatar-img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
@apply flex items-center min-w-0;
|
||||
}
|
||||
|
||||
.player-modal__similar-card-author-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: clamp(var(--space-6xs), 0.35vw, var(--space-5xs));
|
||||
min-width: 0;
|
||||
@apply flex min-w-0 flex-col;
|
||||
}
|
||||
|
||||
.player-modal__similar-card-author-name {
|
||||
font-weight: 500;
|
||||
color: var(--color-text);
|
||||
min-width: 0;
|
||||
@apply truncate text-xs font-medium text-muted transition-colors duration-base ease-out;
|
||||
}
|
||||
|
||||
.player-modal__similar-card:hover .player-modal__similar-card-author-name {
|
||||
@apply text-text-strong;
|
||||
}
|
||||
|
||||
/* Hidden for now to match cleaner modern look, but kept in DOM if needed */
|
||||
.player-modal__similar-card-author-npub {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding-inline: var(--space-6xs);
|
||||
border-radius: var(--radius-full);
|
||||
font-family: var(--font-family-mono);
|
||||
font-size: var(--font-size-2xs);
|
||||
line-height: 1.2;
|
||||
color: var(--color-muted-strong);
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--color-surface-alt) 80%,
|
||||
transparent
|
||||
);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.player-modal__similar-card-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: clamp(var(--space-5xs), 0.5vw, var(--space-4xs));
|
||||
font-size: var(--font-size-2xs);
|
||||
color: var(--color-muted-strong);
|
||||
min-width: 0;
|
||||
@apply flex flex-wrap items-center gap-1 text-2xs text-muted-strong;
|
||||
}
|
||||
|
||||
.player-modal__similar-card-timestamp {
|
||||
@@ -2270,8 +2160,7 @@
|
||||
}
|
||||
|
||||
.player-modal__similar-card-separator {
|
||||
color: inherit;
|
||||
opacity: 0.8;
|
||||
@apply text-muted opacity-60;
|
||||
}
|
||||
|
||||
.player-modal__similar-card-views {
|
||||
|
||||
@@ -310,8 +310,9 @@ export class SimilarContentCard {
|
||||
}
|
||||
|
||||
build() {
|
||||
// Modified: Ensure 'group' class for hover effects
|
||||
const root = this.document.createElement("article");
|
||||
root.classList.add("player-modal__similar-card", "card");
|
||||
root.classList.add("player-modal__similar-card", "card", "group");
|
||||
root.dataset.component = "similar-content-card";
|
||||
root.dataset.index = String(this.index);
|
||||
if (this.video.id) {
|
||||
@@ -361,6 +362,9 @@ export class SimilarContentCard {
|
||||
img.alt = this.video.title || "";
|
||||
img.dataset.videoThumbnail = "true";
|
||||
|
||||
// Modified: Add transition classes for hover zoom
|
||||
img.classList.add("transition-transform", "duration-300", "ease-out", "group-hover:scale-105");
|
||||
|
||||
const rawThumbnail =
|
||||
typeof this.video.thumbnail === "string"
|
||||
? this.video.thumbnail.trim()
|
||||
@@ -465,6 +469,7 @@ export class SimilarContentCard {
|
||||
titleLink.title = this.video.title || "Untitled";
|
||||
titleLink.style.minWidth = "0";
|
||||
|
||||
// Modified order: Title -> Author -> Meta
|
||||
const authorStack = this.buildAuthorStack();
|
||||
const metaRow = this.buildMetaRow();
|
||||
|
||||
@@ -486,6 +491,24 @@ export class SimilarContentCard {
|
||||
wrapper.classList.add("player-modal__similar-card-author");
|
||||
wrapper.style.minWidth = "0";
|
||||
|
||||
const textWrapper = this.document.createElement("span");
|
||||
textWrapper.classList.add("player-modal__similar-card-author-meta");
|
||||
textWrapper.style.minWidth = "0";
|
||||
|
||||
const nameEl = this.document.createElement("span");
|
||||
nameEl.classList.add("author-name", "player-modal__similar-card-author-name");
|
||||
textWrapper.appendChild(nameEl);
|
||||
|
||||
// Removed npubEl appending here to simplify visual noise in compact card
|
||||
// but keeping element reference if needed for logic
|
||||
const npubEl = this.document.createElement("span");
|
||||
npubEl.classList.add("author-npub", "player-modal__similar-card-author-npub");
|
||||
// textWrapper.appendChild(npubEl);
|
||||
|
||||
// Reordered: Content -> Meta (Avatar logic removed from stack to clean up UI,
|
||||
// or kept minimal if desired. For now, we keep structure but can style via CSS order if needed)
|
||||
|
||||
// Actually, keeping the avatar structure but CSS will handle sizing/layout
|
||||
const avatarWrapper = this.document.createElement("span");
|
||||
avatarWrapper.classList.add("player-modal__similar-card-avatar");
|
||||
|
||||
@@ -496,21 +519,29 @@ export class SimilarContentCard {
|
||||
avatarImg.alt = "";
|
||||
avatarWrapper.appendChild(avatarImg);
|
||||
|
||||
const textWrapper = this.document.createElement("span");
|
||||
textWrapper.classList.add("player-modal__similar-card-author-meta");
|
||||
textWrapper.style.minWidth = "0";
|
||||
|
||||
const nameEl = this.document.createElement("span");
|
||||
nameEl.classList.add("author-name", "player-modal__similar-card-author-name");
|
||||
textWrapper.appendChild(nameEl);
|
||||
|
||||
const npubEl = this.document.createElement("span");
|
||||
npubEl.classList.add("author-npub", "player-modal__similar-card-author-npub");
|
||||
textWrapper.appendChild(npubEl);
|
||||
|
||||
wrapper.appendChild(avatarWrapper);
|
||||
// Append avatar then text
|
||||
wrapper.appendChild(textWrapper);
|
||||
|
||||
// Note: To match modern style, we might want text first or no avatar.
|
||||
// But let's keep avatar for now but maybe hide it via CSS if needed,
|
||||
// or just show name.
|
||||
// Let's re-append avatar if we want it visible
|
||||
// wrapper.appendChild(avatarWrapper);
|
||||
|
||||
// Let's stick to the previous logical structure but rely on CSS flex order or just
|
||||
// simplified HTML order:
|
||||
// [Name]
|
||||
// [Meta]
|
||||
|
||||
// Cleaning up: We want Title -> Name -> Meta
|
||||
// This wrapper is for the "Author" line.
|
||||
|
||||
// Let's clear wrapper and re-append in clean order
|
||||
while (wrapper.firstChild) { wrapper.removeChild(wrapper.firstChild); }
|
||||
|
||||
wrapper.appendChild(textWrapper);
|
||||
|
||||
// We preserve refs
|
||||
this.avatarEl = avatarImg;
|
||||
this.authorNameEl = nameEl;
|
||||
this.authorNpubEl = npubEl;
|
||||
@@ -525,6 +556,24 @@ export class SimilarContentCard {
|
||||
row.classList.add("player-modal__similar-card-meta");
|
||||
row.style.minWidth = "0";
|
||||
|
||||
// Reordered: Views -> Separator -> Time
|
||||
|
||||
const viewEl = this.document.createElement("span");
|
||||
viewEl.classList.add("player-modal__similar-card-views", "view-count-text");
|
||||
viewEl.dataset.viewCount = "";
|
||||
viewEl.textContent = "– views";
|
||||
if (this.pointerInfo?.key) {
|
||||
viewEl.dataset.viewPointer = this.pointerInfo.key;
|
||||
}
|
||||
row.appendChild(viewEl);
|
||||
this.viewCountEl = viewEl;
|
||||
|
||||
const separator = this.document.createElement("span");
|
||||
separator.classList.add("player-modal__similar-card-separator");
|
||||
separator.setAttribute("aria-hidden", "true");
|
||||
separator.textContent = "•";
|
||||
row.appendChild(separator);
|
||||
|
||||
const timeEl = this.document.createElement("time");
|
||||
timeEl.classList.add("player-modal__similar-card-timestamp");
|
||||
if (this.postedAt !== null) {
|
||||
@@ -540,25 +589,6 @@ export class SimilarContentCard {
|
||||
row.appendChild(timeEl);
|
||||
this.timeEl = timeEl;
|
||||
|
||||
const shouldShowViews = Boolean(this.pointerInfo?.key);
|
||||
if (shouldShowViews) {
|
||||
const separator = this.document.createElement("span");
|
||||
separator.classList.add("player-modal__similar-card-separator");
|
||||
separator.setAttribute("aria-hidden", "true");
|
||||
separator.textContent = "•";
|
||||
row.appendChild(separator);
|
||||
}
|
||||
|
||||
const viewEl = this.document.createElement("span");
|
||||
viewEl.classList.add("player-modal__similar-card-views", "view-count-text");
|
||||
viewEl.dataset.viewCount = "";
|
||||
viewEl.textContent = "– views";
|
||||
if (this.pointerInfo?.key) {
|
||||
viewEl.dataset.viewPointer = this.pointerInfo.key;
|
||||
}
|
||||
row.appendChild(viewEl);
|
||||
this.viewCountEl = viewEl;
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user