Skip to content

Commit fe85941

Browse files
Merge pull request #62 from pranjal29092005/feat/add-social-share-buttons
feat(article): add social share buttons with all clickable link for all sharing options and pre-filled content
2 parents a5decff + 20d2465 commit fe85941

2 files changed

Lines changed: 151 additions & 9 deletions

File tree

app/a/[slug]/BlogPostClient.tsx

Lines changed: 138 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client"
22

3+
import { useState, useEffect, useRef } from "react"
34
import Link from "next/link"
45
import { ArrowLeft, Calendar, User } from "lucide-react"
56
import Image from "next/image"
@@ -8,6 +9,15 @@ import Footer from "@/components/footer"
89
import { BlogPost } from "@/lib/blog"
910

1011
export default function BlogPostPage({ post }: { post: BlogPost }) {
12+
const [copied, setCopied] = useState(false)
13+
const copiedTimeoutRef = useRef<number | null>(null)
14+
15+
useEffect(() => {
16+
return () => {
17+
if (copiedTimeoutRef.current != null) window.clearTimeout(copiedTimeoutRef.current)
18+
}
19+
}, [])
20+
1121
return (
1222
<div className="min-h-screen bg-gradient-to-br from-green-50 via-yellow-50 to-[#FFC517]/10">
1323
{/* Header */}
@@ -29,15 +39,11 @@ export default function BlogPostPage({ post }: { post: BlogPost }) {
2939
{/* Hero Image */}
3040
{post.image && (
3141
<div className="relative h-64 md:h-96">
32-
<Image
33-
src={
34-
post.image.startsWith("/")
35-
? `${post.image}`
36-
: post.image
37-
}
38-
alt={post.title}
39-
fill
40-
className="object-cover"
42+
<Image
43+
src={post.image}
44+
alt={post.title}
45+
fill
46+
className="object-cover"
4147
priority
4248
/>
4349
<div className="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent" />
@@ -67,6 +73,129 @@ export default function BlogPostPage({ post }: { post: BlogPost }) {
6773
</div>
6874
</div>
6975

76+
{/* Social Share Buttons */}
77+
<div className="mb-8 pb-8 border-b border-gradient-to-r from-[#228B22]/20 to-[#FFBF00]/20">
78+
<p className="text-sm font-medium text-gray-600 mb-4">Share this article:</p>
79+
<div className="flex flex-wrap items-center gap-3">
80+
{/* Twitter/X Share */}
81+
<button
82+
type="button"
83+
onClick={() => {
84+
const text = encodeURIComponent(post.title)
85+
const url = encodeURIComponent(window.location.href)
86+
const shareWindow = window.open(
87+
`https://twitter.com/intent/tweet?text=${text}&url=${url}`,
88+
'_blank',
89+
'noopener,noreferrer,width=550,height=420'
90+
)
91+
if (!shareWindow) {
92+
alert('Please allow popups to share this article.')
93+
}
94+
}}
95+
className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-gradient-to-r from-[#228B22] to-[#5A981A] hover:from-[#3E921E] hover:to-[#91A511] text-white text-sm font-medium transition-all duration-300 shadow-md hover:shadow-lg transform hover:-translate-y-0.5"
96+
aria-label="Share on Twitter"
97+
>
98+
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
99+
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
100+
</svg>
101+
Twitter
102+
</button>
103+
104+
{/* LinkedIn Share */}
105+
<button
106+
type="button"
107+
onClick={() => {
108+
const url = encodeURIComponent(window.location.href)
109+
const title = encodeURIComponent(post.title)
110+
const summary = encodeURIComponent(post.excerpt || '')
111+
const shareWindow = window.open(
112+
`https://www.linkedin.com/sharing/share-offsite/?url=${url}&title=${title}&summary=${summary}`,
113+
'_blank',
114+
'noopener,noreferrer,width=550,height=420'
115+
)
116+
if (!shareWindow) {
117+
alert('Please allow popups to share this article.')
118+
}
119+
}}
120+
className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-gradient-to-r from-[#91A511] to-[#ADAC0D] hover:from-[#ADAC0D] hover:to-[#C8B209] text-white text-sm font-medium transition-all duration-300 shadow-md hover:shadow-lg transform hover:-translate-y-0.5"
121+
aria-label="Share on LinkedIn"
122+
>
123+
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
124+
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
125+
</svg>
126+
LinkedIn
127+
</button>
128+
129+
{/* Facebook Share */}
130+
<button
131+
type="button"
132+
onClick={() => {
133+
const url = encodeURIComponent(window.location.href)
134+
const quote = encodeURIComponent(post.title)
135+
const shareWindow = window.open(
136+
`https://www.facebook.com/sharer/sharer.php?u=${url}&quote=${quote}`,
137+
'_blank',
138+
'noopener,noreferrer,width=550,height=420'
139+
)
140+
if (!shareWindow) {
141+
alert('Please allow popups to share this article.')
142+
}
143+
}}
144+
className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-gradient-to-r from-[#C8B209] to-[#E4B905] hover:from-[#E4B905] hover:to-[#FFBF00] text-white text-sm font-medium transition-all duration-300 shadow-md hover:shadow-lg transform hover:-translate-y-0.5"
145+
aria-label="Share on Facebook"
146+
>
147+
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
148+
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z" />
149+
</svg>
150+
Facebook
151+
</button>
152+
153+
{/* Copy Link */}
154+
<button
155+
type="button"
156+
onClick={() => {
157+
if (!window.isSecureContext || !navigator.clipboard?.writeText) {
158+
alert('Copy is only available in a secure context (HTTPS or localhost). Please copy the URL from the address bar.')
159+
return
160+
}
161+
navigator.clipboard.writeText(window.location.href)
162+
.then(() => {
163+
if (copiedTimeoutRef.current != null) window.clearTimeout(copiedTimeoutRef.current)
164+
setCopied(true)
165+
copiedTimeoutRef.current = window.setTimeout(() => setCopied(false), 2000)
166+
})
167+
.catch((err) => {
168+
console.error('Failed to copy:', err)
169+
alert('Failed to copy link. Please copy manually.')
170+
})
171+
}}
172+
className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-gradient-to-r from-[#E4B905] to-[#FFBF00] hover:from-[#FFBF00] hover:to-[#FFC517] text-white text-sm font-medium transition-all duration-300 shadow-md hover:shadow-lg transform hover:-translate-y-0.5"
173+
aria-label="Copy link to clipboard"
174+
>
175+
{copied ? (
176+
<>
177+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
178+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
179+
</svg>
180+
Copied!
181+
</>
182+
) : (
183+
<>
184+
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
185+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
186+
</svg>
187+
Copy Link
188+
</>
189+
)}
190+
</button>
191+
</div>
192+
</div>
193+
194+
{/* Screen reader announcement for copy feedback */}
195+
<div role="status" aria-live="polite" className="sr-only">
196+
{copied && "Link copied to clipboard"}
197+
</div>
198+
70199
<div className="prose prose-lg max-w-none">
71200
<MarkdownRenderer content={post.content} />
72201
</div>

app/globals.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,19 @@ body {
1010
.text-balance {
1111
text-wrap: balance;
1212
}
13+
14+
/* Screen reader only - hides content visually but keeps it accessible to screen readers */
15+
.sr-only {
16+
position: absolute;
17+
width: 1px;
18+
height: 1px;
19+
padding: 0;
20+
margin: -1px;
21+
overflow: hidden;
22+
clip: rect(0, 0, 0, 0);
23+
white-space: nowrap;
24+
border-width: 0;
25+
}
1326
}
1427

1528
@layer base {

0 commit comments

Comments
 (0)