Skip to content

Commit b00bd2e

Browse files
authored
Merge pull request #1720 from recodehive/copilot/update-avatar-stack-format
feat: replace single-author display with clickable avatar stack + full names on blog cards
2 parents de61cf5 + 54c1197 commit b00bd2e

2 files changed

Lines changed: 93 additions & 37 deletions

File tree

src/pages/blogs/blogs-new.css

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,28 @@
862862
z-index: 2;
863863
}
864864

865+
/* Avatar stack */
866+
.card-avatar-stack {
867+
display: flex;
868+
align-items: center;
869+
flex-direction: row;
870+
flex-shrink: 0;
871+
}
872+
873+
.card-avatar-stack .card-avatar {
874+
margin-left: -8px;
875+
transition: transform 0.2s ease;
876+
}
877+
878+
.card-avatar-stack .card-avatar:first-child {
879+
margin-left: 0;
880+
}
881+
882+
.card-avatar-stack .card-avatar:hover {
883+
transform: scale(1.1);
884+
z-index: 10 !important;
885+
}
886+
865887
/* Avatar */
866888
.card-avatar {
867889
width: 34px;
@@ -910,14 +932,31 @@
910932
min-width: 0;
911933
}
912934

935+
.card-author-names {
936+
display: flex;
937+
flex-wrap: wrap;
938+
align-items: center;
939+
gap: 0;
940+
min-width: 0;
941+
overflow: hidden;
942+
}
943+
944+
.card-author-sep {
945+
color: #94a3b8;
946+
font-size: 12px;
947+
white-space: pre;
948+
}
949+
950+
[data-theme="dark"] .card-author-sep {
951+
color: #64748b;
952+
}
953+
913954
.card-author-handle {
914955
font-size: 12px;
915956
font-weight: 600;
916957
color: #374151;
917958
text-decoration: none;
918959
white-space: nowrap;
919-
overflow: hidden;
920-
text-overflow: ellipsis;
921960
transition: color 0.2s ease;
922961
position: relative;
923962
z-index: 2;

src/pages/blogs/index.tsx

Lines changed: 52 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,6 @@ export default function Blogs() {
233233

234234
const BlogCard = ({ blog }: { blog: (typeof blogs)[number] }) => {
235235
const authors = getAuthorProfiles(blog.authors || []);
236-
// Use only the first author for the bottom row (matches reference design)
237-
const primaryAuthor = authors[0];
238236

239237
// Tags — use blog.tags if present, fallback to blog.category as single tag
240238
const tags: string[] =
@@ -289,45 +287,64 @@ const BlogCard = ({ blog }: { blog: (typeof blogs)[number] }) => {
289287
</div>
290288
)}
291289

292-
{/* Bottom row: avatar + author + date ··· Read → */}
290+
{/* Bottom row: avatar stack + author names + date ··· Read → */}
293291
<div className="card-footer">
294292
<div className="card-author-row">
295-
{/* Avatar */}
296-
{primaryAuthor && (
297-
<div className="card-avatar">
298-
{primaryAuthor.imageUrl ? (
299-
<img
300-
src={primaryAuthor.imageUrl}
301-
alt={primaryAuthor.name}
302-
className="card-avatar-img"
303-
onError={(e) => {
304-
const t = e.currentTarget;
305-
t.style.display = "none";
306-
const fb = t.nextElementSibling as HTMLElement | null;
307-
if (fb) fb.style.display = "flex";
308-
}}
309-
/>
310-
) : null}
311-
<span
312-
className="card-avatar-fallback"
313-
style={{ display: primaryAuthor.imageUrl ? "none" : "flex" }}
314-
>
315-
{primaryAuthor.name.charAt(0).toUpperCase()}
316-
</span>
293+
{/* Avatar stack */}
294+
{authors.length > 0 && (
295+
<div className="card-avatar-stack">
296+
{authors.slice(0, 3).map((author, idx) => (
297+
<Link
298+
key={author.id}
299+
href={author.githubUrl}
300+
className="card-avatar"
301+
style={{ zIndex: authors.length - idx } as React.CSSProperties}
302+
target="_blank"
303+
rel="noopener noreferrer"
304+
title={author.name}
305+
>
306+
{author.imageUrl ? (
307+
<img
308+
src={author.imageUrl}
309+
alt={author.name}
310+
className="card-avatar-img"
311+
onError={(e) => {
312+
const t = e.currentTarget;
313+
t.style.display = "none";
314+
const fb = t.nextElementSibling as HTMLElement | null;
315+
if (fb) fb.style.display = "flex";
316+
}}
317+
/>
318+
) : null}
319+
<span
320+
className="card-avatar-fallback"
321+
style={{ display: author.imageUrl ? "none" : "flex" }}
322+
>
323+
{author.name.charAt(0).toUpperCase()}
324+
</span>
325+
</Link>
326+
))}
317327
</div>
318328
)}
319329

320-
{/* Name + date stacked */}
330+
{/* Author names + date stacked */}
321331
<div className="card-author-info">
322-
{primaryAuthor && (
323-
<Link
324-
href={primaryAuthor.githubUrl}
325-
className="card-author-handle"
326-
target="_blank"
327-
rel="noopener noreferrer"
328-
>
329-
By @{primaryAuthor.id || primaryAuthor.name.toLowerCase().replace(/\s+/g, "")}
330-
</Link>
332+
{authors.length > 0 && (
333+
<span className="card-author-names">
334+
{authors.map((author, idx) => (
335+
<React.Fragment key={author.id}>
336+
{idx > 0 && <span className="card-author-sep">, </span>}
337+
<Link
338+
href={author.githubUrl}
339+
className="card-author-handle"
340+
target="_blank"
341+
rel="noopener noreferrer"
342+
>
343+
{author.name}
344+
</Link>
345+
</React.Fragment>
346+
))}
347+
</span>
331348
)}
332349
{(blog as any).date && (
333350
<span className="card-date">{formatDate((blog as any).date)}</span>

0 commit comments

Comments
 (0)