Skip to content

Commit b3f511c

Browse files
authored
Merge pull request #1627 from steam-bell-92/main
Adds author & blog details at end of blogs
2 parents 5a9423d + a67655d commit b3f511c

2 files changed

Lines changed: 230 additions & 0 deletions

File tree

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import React from "react";
2+
import Link from "@docusaurus/Link";
3+
import { useBlogPost } from "@docusaurus/plugin-content-blog/client";
4+
import BlogPostItemFooterOriginal from "@theme-original/BlogPostItem/Footer";
5+
import type BlogPostItemFooterType from "@theme/BlogPostItem/Footer";
6+
import type { WrapperProps } from "@docusaurus/types";
7+
import { getAuthorProfile } from "@site/src/utils/authors";
8+
9+
import styles from "./styles.module.css";
10+
11+
type Props = WrapperProps<typeof BlogPostItemFooterType>;
12+
13+
function getGitHubUrl(author: {
14+
key?: string;
15+
url?: string;
16+
name?: string;
17+
}): string | undefined {
18+
if (author.key) {
19+
return getAuthorProfile(author.key).githubUrl;
20+
}
21+
22+
if (author.url && /github\.com\//i.test(author.url)) {
23+
return author.url.startsWith("http") ? author.url : `https://${author.url}`;
24+
}
25+
26+
return undefined;
27+
}
28+
29+
export default function BlogPostItemFooterWrapper(props: Props): JSX.Element {
30+
const { metadata, isBlogPostPage } = useBlogPost();
31+
const primaryAuthor = metadata.authors?.[0];
32+
33+
const profile = primaryAuthor?.key
34+
? getAuthorProfile(primaryAuthor.key)
35+
: undefined;
36+
37+
const authorName = primaryAuthor?.name || profile?.name;
38+
const authorAvatar = primaryAuthor?.imageURL || profile?.imageUrl;
39+
const githubUrl = primaryAuthor
40+
? getGitHubUrl({
41+
key: primaryAuthor.key,
42+
name: primaryAuthor.name,
43+
url: primaryAuthor.url,
44+
})
45+
: undefined;
46+
47+
const roundedReadTime = Math.max(1, Math.ceil(metadata.readingTime || 0));
48+
const readTimeText = `${roundedReadTime} min read`;
49+
const authorSummary =
50+
primaryAuthor?.description ||
51+
profile?.description ||
52+
primaryAuthor?.title ||
53+
profile?.title;
54+
const blogDate =
55+
metadata.formattedDate ||
56+
(metadata.date
57+
? new Date(metadata.date).toLocaleDateString("en-US", {
58+
year: "numeric",
59+
month: "short",
60+
day: "numeric",
61+
})
62+
: "");
63+
64+
const showAuthorCard = Boolean(isBlogPostPage && primaryAuthor && authorName);
65+
66+
return (
67+
<>
68+
<BlogPostItemFooterOriginal {...props} />
69+
{showAuthorCard && (
70+
<section className={styles.authorCard} aria-label="Post author details">
71+
<div className={styles.authorLeft}>
72+
{authorAvatar ? (
73+
<img
74+
className={styles.authorAvatar}
75+
src={authorAvatar}
76+
alt={`${authorName} profile picture`}
77+
loading="lazy"
78+
/>
79+
) : (
80+
<div className={styles.authorAvatarFallback} aria-hidden="true">
81+
{authorName?.charAt(0).toUpperCase()}
82+
</div>
83+
)}
84+
<div className={styles.authorIdentity}>
85+
<p className={styles.authorName}>{authorName}</p>
86+
{blogDate ? <p className={styles.authorDate}>{blogDate}</p> : null}
87+
{authorSummary ? (
88+
<p className={styles.authorSummary}>{authorSummary}</p>
89+
) : null}
90+
</div>
91+
</div>
92+
93+
<div className={styles.authorRight}>
94+
<p className={styles.readTime}>{readTimeText}</p>
95+
{githubUrl ? (
96+
<Link
97+
className={styles.githubButton}
98+
to={githubUrl}
99+
target="_blank"
100+
rel="noopener noreferrer"
101+
>
102+
GitHub Profile
103+
</Link>
104+
) : null}
105+
</div>
106+
</section>
107+
)}
108+
</>
109+
);
110+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
.authorCard {
2+
position: relative;
3+
width: 100%;
4+
margin-top: 1.25rem;
5+
padding: 1rem;
6+
padding-bottom: 3.75rem;
7+
border: 1px solid var(--ifm-color-emphasis-300);
8+
border-radius: 12px;
9+
display: flex;
10+
align-items: stretch;
11+
justify-content: space-between;
12+
gap: 1rem;
13+
background: var(--ifm-background-surface-color);
14+
box-sizing: border-box;
15+
}
16+
17+
.authorLeft {
18+
display: flex;
19+
align-items: center;
20+
gap: 0.75rem;
21+
min-width: 0;
22+
}
23+
24+
.authorAvatar,
25+
.authorAvatarFallback {
26+
width: 52px;
27+
height: 52px;
28+
border-radius: 50%;
29+
flex-shrink: 0;
30+
}
31+
32+
.authorAvatar {
33+
object-fit: cover;
34+
border: 1px solid var(--ifm-color-emphasis-300);
35+
}
36+
37+
.authorAvatarFallback {
38+
display: grid;
39+
place-items: center;
40+
font-weight: 700;
41+
color: var(--ifm-color-primary-darkest);
42+
background: var(--ifm-color-emphasis-200);
43+
}
44+
45+
.authorIdentity {
46+
min-width: 0;
47+
}
48+
49+
.authorName {
50+
margin: 0;
51+
font-weight: 700;
52+
line-height: 1.2;
53+
}
54+
55+
.authorDate {
56+
margin: 0.25rem 0 0;
57+
color: var(--ifm-color-emphasis-700);
58+
font-size: 0.9rem;
59+
}
60+
61+
.authorSummary {
62+
margin: 0.45rem 0 0;
63+
color: var(--ifm-color-emphasis-800);
64+
font-size: 0.92rem;
65+
line-height: 1.4;
66+
max-width: 56ch;
67+
}
68+
69+
.authorRight {
70+
margin-left: auto;
71+
display: flex;
72+
flex-direction: column;
73+
align-items: flex-end;
74+
justify-content: flex-start;
75+
gap: 0.75rem;
76+
text-align: right;
77+
}
78+
79+
.readTime {
80+
margin: 0;
81+
color: var(--ifm-color-emphasis-700);
82+
font-weight: 600;
83+
}
84+
85+
.githubButton {
86+
position: absolute;
87+
right: 1rem;
88+
bottom: 1rem;
89+
display: inline-flex;
90+
align-items: center;
91+
justify-content: center;
92+
padding: 0.45rem 0.8rem;
93+
border-radius: 8px;
94+
border: 1px solid var(--ifm-color-primary);
95+
font-weight: 600;
96+
text-decoration: none;
97+
}
98+
99+
.githubButton:hover {
100+
text-decoration: none;
101+
}
102+
103+
@media (max-width: 640px) {
104+
.authorCard {
105+
padding-bottom: 1rem;
106+
flex-direction: column;
107+
align-items: flex-start;
108+
}
109+
110+
.authorRight {
111+
width: 100%;
112+
margin-left: 0;
113+
align-items: flex-start;
114+
text-align: left;
115+
}
116+
117+
.githubButton {
118+
position: static;
119+
}
120+
}

0 commit comments

Comments
 (0)