From 3d74c25c7debd69c318f60b0b0d91d3eeb25c0cf Mon Sep 17 00:00:00 2001 From: Gigi Date: Sat, 25 Oct 2025 01:30:23 +0200 Subject: [PATCH] feat: enhance medium-sized bookmark cards with improved styling and layout - Add card-view class for better visual hierarchy - Implement hero image display with fallback placeholder - Add responsive design for mobile and tablet screens - Improve content truncation with line clamping - Enhance URL display with better styling - Add hover effects and smooth transitions - Optimize card layout for better readability --- src/components/BookmarkViews/CardView.tsx | 216 ++++++++++++---------- src/styles/components/cards.css | 156 ++++++++++++++++ 2 files changed, 270 insertions(+), 102 deletions(-) diff --git a/src/components/BookmarkViews/CardView.tsx b/src/components/BookmarkViews/CardView.tsx index 23412586..c5ff8667 100644 --- a/src/components/BookmarkViews/CardView.tsx +++ b/src/components/BookmarkViews/CardView.tsx @@ -71,6 +71,10 @@ export const CardView: React.FC = ({ } }, [firstUrl, articleImage, instantPreview, ogImage]) + // Add loading state for images + const [imageLoading, setImageLoading] = useState(false) + const [imageError, setImageError] = useState(false) + const triggerOpen = () => handleReadNow({ preventDefault: () => {} } as React.MouseEvent) const handleKeyDown: React.KeyboardEventHandler = (e) => { @@ -106,122 +110,130 @@ export const CardView: React.FC = ({ return (
- {cachedImage && ( + {(cachedImage || firstUrl) && (
handleReadNow({ preventDefault: () => {} } as React.MouseEvent)} - /> - )} -
- - - - - {getInternalRoute() ? ( - e.stopPropagation()} - > - {formatDate(bookmark.created_at ?? bookmark.listUpdatedAt)} - - ) : ( - {formatDate(bookmark.created_at ?? bookmark.listUpdatedAt)} - )} -
- - {extractedUrls.length > 0 && ( -
- {(urlsExpanded ? extractedUrls : extractedUrls.slice(0, 1)).map((url, urlIndex) => { - return ( - - ) - })} - {extractedUrls.length > 1 && ( - + > + {!cachedImage && firstUrl && ( +
+ +
)}
)} - - {isArticle && articleSummary ? ( - - ) : bookmark.parsedContent ? ( -
- {shouldTruncate && bookmark.content - ? - : renderParsedContent(bookmark.parsedContent)} +
+
+ + + + + {getInternalRoute() ? ( + e.stopPropagation()} + > + {formatDate(bookmark.created_at ?? bookmark.listUpdatedAt)} + + ) : ( + {formatDate(bookmark.created_at ?? bookmark.listUpdatedAt)} + )}
- ) : bookmark.content && ( - - )} + + {extractedUrls.length > 0 && ( +
+ {(urlsExpanded ? extractedUrls : extractedUrls.slice(0, 1)).map((url, urlIndex) => { + return ( + + ) + })} + {extractedUrls.length > 1 && ( + + )} +
+ )} + + {isArticle && articleSummary ? ( + + ) : bookmark.parsedContent ? ( +
+ {shouldTruncate && bookmark.content + ? + : renderParsedContent(bookmark.parsedContent)} +
+ ) : bookmark.content && ( + + )} - {contentLength > 210 && ( - - )} - - {/* Reading progress indicator for articles */} - {isArticle && readingProgress !== undefined && readingProgress > 0 && ( -
-
-
- )} - -
-
- e.stopPropagation()} + {contentLength > 210 && ( + + )} + + {/* Reading progress indicator for articles */} + {isArticle && readingProgress !== undefined && readingProgress > 0 && ( +
+
+
+ )} + +
+
+ e.stopPropagation()} + > + {getAuthorDisplayName()} + +
+ {/* CTA removed */}
- {/* CTA removed */}
) diff --git a/src/styles/components/cards.css b/src/styles/components/cards.css index 8a350e5b..a1db717b 100644 --- a/src/styles/components/cards.css +++ b/src/styles/components/cards.css @@ -76,6 +76,123 @@ .expand-toggle-urls { margin-top: 0.5rem; background: transparent; border: none; color: var(--color-primary); cursor: pointer; font-size: 0.8rem; padding: 0.25rem 0; text-decoration: underline; } .expand-toggle-urls:hover { color: var(--color-primary-hover); } +/* Medium-sized card view */ +.individual-bookmark.card-view { + padding: 0; + display: flex; + flex-direction: column; + overflow: hidden; + border: 1px solid var(--color-bg-elevated); + border-radius: 12px; + transition: all 0.3s ease; + background: var(--color-bg); +} + +.individual-bookmark.card-view:hover { + border-color: var(--color-border); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + transform: translateY(-2px); +} + +.card-hero-image { + width: 100%; + height: 160px; + background: linear-gradient(135deg, var(--color-bg-elevated) 0%, var(--color-bg-subtle) 50%, var(--color-bg-elevated) 100%); + background-size: cover; + background-position: center; + background-repeat: no-repeat; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.3s ease; + border-bottom: 1px solid var(--color-border); + position: relative; +} + +.card-hero-image:hover { + opacity: 0.9; +} + +.card-hero-image::after { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(to bottom, transparent 60%, rgba(0,0,0,0.2) 100%); + pointer-events: none; +} + +.card-hero-image .preview-placeholder { + font-size: 2.5rem; + color: var(--color-border-subtle); + opacity: 0.6; + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; +} + +.card-content { + padding: 1.25rem; + display: flex; + flex-direction: column; + gap: 0.75rem; + flex: 1; +} + +.card-content .bookmark-header { + margin-bottom: 0.5rem; +} + +.card-content .bookmark-content { + font-size: 0.9rem; + line-height: 1.6; + color: var(--color-text); + margin: 0; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.card-content .bookmark-content.article-summary { + -webkit-line-clamp: 2; + font-style: italic; + color: var(--color-text-secondary); +} + +.card-content .bookmark-footer { + margin-top: auto; + padding-top: 0.75rem; + border-top: 1px solid var(--color-border); +} + +.card-content .bookmark-urls { + margin: 0.5rem 0; +} + +.card-content .bookmark-url { + display: block; + width: 100%; + text-align: left; + padding: 0.5rem; + margin-bottom: 0.25rem; + background: var(--color-bg-elevated); + border: 1px solid var(--color-border); + border-radius: 6px; + color: var(--color-primary); + text-decoration: none; + font-size: 0.8rem; + word-break: break-all; + transition: all 0.2s ease; +} + +.card-content .bookmark-url:hover { + background: var(--color-bg); + border-color: var(--color-primary); +} + /* Large preview view */ .individual-bookmark.large { padding: 0; display: flex; flex-direction: column; overflow: hidden; border: 1px solid var(--color-bg-elevated); } .large-preview-image { width: 100%; height: 180px; background: linear-gradient(135deg, var(--color-bg-elevated) 0%, var(--color-bg-subtle) 50%, var(--color-bg-elevated) 100%); background-size: cover; background-position: center; background-repeat: no-repeat; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.2s ease; border-bottom: 1px solid var(--color-border); position: relative; } @@ -115,6 +232,45 @@ .blog-post-card-meta { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding-top: 0.75rem; border-top: 1px solid var(--color-border); font-size: 0.75rem; color: var(--color-text-muted); flex-wrap: wrap; } .blog-post-card-author, .blog-post-card-date { display: flex; align-items: center; gap: 0.5rem; } .blog-post-card-author svg, .blog-post-card-date svg { opacity: 0.7; } +/* Responsive design for medium-sized cards */ +@media (max-width: 768px) { + .individual-bookmark.card-view { + border-radius: 8px; + } + + .card-hero-image { + height: 140px; + } + + .card-content { + padding: 1rem; + gap: 0.5rem; + } + + .card-content .bookmark-content { + font-size: 0.85rem; + line-height: 1.5; + } + + .card-content .bookmark-footer { + padding-top: 0.5rem; + } +} + +@media (max-width: 480px) { + .card-hero-image { + height: 120px; + } + + .card-content { + padding: 0.75rem; + } + + .card-content .bookmark-content { + font-size: 0.8rem; + } +} + @media (max-width: 768px) { .explore-container { padding: 1rem; } .explore-header h1 { font-size: 2rem; }