Skip to content

Commit 3ceb2b1

Browse files
authored
feat: add blog post social share buttons
1 parent d75415f commit 3ceb2b1

3 files changed

Lines changed: 154 additions & 88 deletions

File tree

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,83 @@
11
.blog-post-share-section {
2-
margin: 3rem 0;
3-
padding: 2rem;
4-
background: rgba(var(--ifm-color-primary-rgb), 0.05);
5-
border-radius: 16px;
6-
text-align: center;
7-
border: 1px solid rgba(var(--ifm-color-primary-rgb), 0.1);
2+
margin-top: 2rem;
3+
padding-top: 1.5rem;
4+
border-top: 1px solid var(--ifm-color-emphasis-300);
85
}
96

107
.share-title {
11-
margin-bottom: 1.5rem;
12-
font-size: 1.25rem;
13-
font-weight: 700;
14-
color: var(--ifm-font-color-base);
8+
margin: 0 0 1rem;
9+
font-size: 0.95rem;
10+
font-weight: 600;
11+
color: var(--ifm-color-emphasis-700);
1512
}
1613

1714
.share-buttons-row {
1815
display: flex;
19-
flex-wrap: wrap;
16+
flex-wrap: nowrap;
2017
gap: 1rem;
21-
justify-content: center;
18+
align-items: center;
2219
}
2320

24-
.share-btn-large {
25-
display: flex;
21+
.share-btn-circle {
22+
display: inline-flex;
2623
align-items: center;
27-
gap: 0.75rem;
28-
padding: 0.8rem 1.5rem;
29-
border-radius: 50px;
30-
color: white !important;
31-
font-weight: 600;
24+
justify-content: center;
25+
width: 3.25rem;
26+
height: 3.25rem;
27+
border-radius: 999px;
28+
border: 1px solid color-mix(in srgb, var(--ifm-color-primary) 18%, var(--ifm-color-emphasis-300));
29+
background: var(--ifm-background-surface-color);
30+
color: var(--ifm-font-color-base) !important;
3231
text-decoration: none !important;
33-
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
34-
font-size: 0.95rem;
35-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
32+
transition:
33+
transform 0.2s ease,
34+
box-shadow 0.2s ease,
35+
border-color 0.2s ease,
36+
color 0.2s ease;
37+
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.06);
38+
cursor: pointer;
3639
}
3740

38-
.share-btn-large:hover {
39-
transform: translateY(-3px);
40-
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
41-
filter: brightness(1.1);
41+
.share-btn-circle svg {
42+
width: 1.15rem;
43+
height: 1.15rem;
4244
}
4345

44-
.share-btn-large.twitter {
45-
background-color: #000000;
46+
.share-btn-circle:hover {
47+
transform: translateY(-2px);
48+
box-shadow: 0 18px 36px rgba(15, 23, 42, 0.1);
49+
text-decoration: none;
4650
}
4751

48-
.share-btn-large.linkedin {
49-
background-color: #0077b5;
52+
.share-btn-circle:focus-visible {
53+
outline: 2px solid var(--ifm-color-primary);
54+
outline-offset: 2px;
5055
}
5156

52-
.share-btn-large.facebook {
53-
background-color: #1877f2;
57+
.share-btn-circle.x:hover {
58+
border-color: #111827;
59+
color: #111827 !important;
60+
}
61+
62+
.share-btn-circle.linkedin:hover {
63+
border-color: #0a66c2;
64+
color: #0a66c2 !important;
65+
}
66+
67+
.share-btn-circle.email:hover {
68+
border-color: #ea580c;
69+
color: #ea580c !important;
70+
}
71+
72+
.share-btn-circle.copy:hover,
73+
.share-btn-circle.copy.copied {
74+
border-color: var(--ifm-color-primary);
75+
color: var(--ifm-color-primary) !important;
5476
}
5577

5678
@media (max-width: 576px) {
5779
.share-buttons-row {
58-
flex-direction: column;
59-
align-items: stretch;
60-
}
61-
62-
.share-btn-large {
63-
justify-content: center;
80+
gap: 0.75rem;
81+
flex-wrap: wrap;
6482
}
6583
}
Lines changed: 94 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,102 @@
1-
import React from 'react';
2-
import { useBlogPost } from '@docusaurus/plugin-content-blog/client';
3-
import { FaTwitter, FaLinkedin, FaFacebook } from 'react-icons/fa';
4-
import './SocialShare.css';
5-
6-
const SocialShare = () => {
7-
// Safe hook call
8-
let blogPost;
9-
try {
10-
blogPost = useBlogPost();
11-
} catch (e) {
1+
import React, { useEffect, useState } from "react";
2+
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
3+
import { FaEnvelope, FaLink, FaLinkedinIn } from "react-icons/fa";
4+
import { FaXTwitter } from "react-icons/fa6";
5+
6+
import "./SocialShare.css";
7+
8+
const COPY_RESET_DELAY_MS = 2000;
9+
10+
type SocialShareProps = {
11+
permalink?: string;
12+
title?: string;
13+
};
14+
15+
export default function SocialShare({
16+
permalink,
17+
title,
18+
}: SocialShareProps): JSX.Element | null {
19+
const { siteConfig } = useDocusaurusContext();
20+
const [copied, setCopied] = useState(false);
21+
22+
useEffect(() => {
23+
if (!copied) {
24+
return undefined;
25+
}
26+
27+
const timeout = window.setTimeout(() => setCopied(false), COPY_RESET_DELAY_MS);
28+
29+
return () => window.clearTimeout(timeout);
30+
}, [copied]);
31+
32+
if (!permalink || !title) {
1233
return null;
1334
}
14-
15-
if (!blogPost) return null;
16-
17-
const { metadata } = blogPost;
18-
const { permalink, title } = metadata;
19-
const blogUrl = `https://www.recodehive.com${permalink}`;
20-
const shareText = encodeURIComponent(`Check out this article: ${title}`);
35+
36+
const blogUrl = new URL(permalink, siteConfig.url).toString();
37+
const shareText = `Check out this article: ${title}`;
38+
const encodedBlogUrl = encodeURIComponent(blogUrl);
39+
const encodedShareText = encodeURIComponent(shareText);
40+
41+
const shareLinks = [
42+
{
43+
className: "x",
44+
href: `https://twitter.com/intent/tweet?text=${encodedShareText}&url=${encodedBlogUrl}`,
45+
icon: <FaXTwitter aria-hidden="true" />,
46+
label: "Share on X",
47+
},
48+
{
49+
className: "linkedin",
50+
href: `https://www.linkedin.com/sharing/share-offsite/?url=${encodedBlogUrl}`,
51+
icon: <FaLinkedinIn aria-hidden="true" />,
52+
label: "Share on LinkedIn",
53+
},
54+
{
55+
className: "email",
56+
href: `mailto:?subject=${encodedShareText}&body=${encodeURIComponent(`${shareText}\n\n${blogUrl}`)}`,
57+
icon: <FaEnvelope aria-hidden="true" />,
58+
label: "Share by email",
59+
},
60+
];
61+
62+
const handleCopyLink = async () => {
63+
if (typeof navigator === "undefined" || !navigator.clipboard) {
64+
return;
65+
}
66+
67+
await navigator.clipboard.writeText(blogUrl);
68+
setCopied(true);
69+
};
2170

2271
return (
23-
<div className="blog-post-share-section">
24-
<h3 className="share-title">Enjoyed the article? Share it!</h3>
72+
<section className="blog-post-share-section" aria-label="Share this post">
73+
<p className="share-title">Share this post</p>
2574
<div className="share-buttons-row">
26-
<a
27-
href={`https://twitter.com/intent/tweet?text=${shareText}&url=${encodeURIComponent(blogUrl)}`}
28-
target="_blank"
29-
rel="noopener noreferrer"
30-
className="share-btn-large twitter"
31-
title="Share on X (Twitter)"
75+
{shareLinks.map((link) => (
76+
<a
77+
key={link.label}
78+
href={link.href}
79+
target="_blank"
80+
rel="noopener noreferrer"
81+
className={`share-btn-circle ${link.className}`}
82+
title={link.label}
83+
aria-label={link.label}
84+
>
85+
{link.icon}
86+
</a>
87+
))}
88+
<button
89+
type="button"
90+
className={`share-btn-circle copy${copied ? " copied" : ""}`}
91+
onClick={() => {
92+
void handleCopyLink();
93+
}}
94+
title={copied ? "Link copied" : "Copy link"}
95+
aria-label={copied ? "Link copied" : "Copy link"}
3296
>
33-
<FaTwitter /> <span>Share on X</span>
34-
</a>
35-
<a
36-
href={`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(blogUrl)}`}
37-
target="_blank"
38-
rel="noopener noreferrer"
39-
className="share-btn-large linkedin"
40-
title="Share on LinkedIn"
41-
>
42-
<FaLinkedin /> <span>Share on LinkedIn</span>
43-
</a>
44-
<a
45-
href={`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(blogUrl)}`}
46-
target="_blank"
47-
rel="noopener noreferrer"
48-
className="share-btn-large facebook"
49-
title="Share on Facebook"
50-
>
51-
<FaFacebook /> <span>Share on Facebook</span>
52-
</a>
97+
<FaLink aria-hidden="true" />
98+
</button>
5399
</div>
54-
</div>
100+
</section>
55101
);
56-
};
57-
58-
export default SocialShare;
102+
}

src/theme/BlogPostItem/Footer/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useBlogPost } from "@docusaurus/plugin-content-blog/client";
55
import BlogPostItemFooterOriginal from "@theme-original/BlogPostItem/Footer";
66
import type BlogPostItemFooterType from "@theme/BlogPostItem/Footer";
77
import type { WrapperProps } from "@docusaurus/types";
8+
import SocialShare from "../../../components/SocialShare";
89
import { getAuthorProfile } from "../../../utils/authors";
910

1011
import styles from "./styles.module.css";
@@ -94,6 +95,9 @@ export default function BlogPostItemFooterWrapper(props: Props): JSX.Element {
9495
return (
9596
<>
9697
<BlogPostItemFooterOriginal {...props} />
98+
{isBlogPostPage && (
99+
<SocialShare permalink={metadata.permalink} title={metadata.title} />
100+
)}
97101
{showAuthorCard && (
98102
<section className={styles.authorCard} aria-label="Post author details">
99103
<div className={styles.authorBody}>

0 commit comments

Comments
 (0)