Skip to content

Commit b072f6d

Browse files
committed
fix(giscus): Sync theme with site color mode
The Giscus component did not update its theme when the site's color mode was toggled. The theme was only set once during the initial script injection. This change implements the recommended approach for dynamic theme updates by: 1. Using a hook that runs only once on mount to inject the Giscus script with the initial theme. 2. Adding a second hook that listens for changes to . 3. When a change is detected, it sends a to the Giscus iframe to update its theme, ensuring it stays in sync with the site's theme toggle. Fixes #775
1 parent 453dacd commit b072f6d

File tree

3 files changed

+110
-15
lines changed

3 files changed

+110
-15
lines changed

src/components/blogCarousel/blogCard.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,34 +37,34 @@ const BlogCard = ({
3737
initial={{ opacity: 0, y: 40, scale: 0.95 }}
3838
animate={{ opacity: 1, y: 0, scale: 1 }}
3939
transition={{ duration: 0.6, ease: "easeOut" }}
40-
whileHover={{
41-
y: -8,
40+
whileHover={{
41+
y: -8,
4242
scale: 1.02,
4343
transition: { duration: 0.4, ease: "easeOut" }
4444
}}
4545
onMouseEnter={() => setIsHovered(true)}
4646
onMouseLeave={() => setIsHovered(false)}
4747
className="relative overflow-hidden h-full transition-all duration-300"
4848
>
49-
<Link
49+
<Link
5050
to={`/blog/${id}`}
5151
className="block h-full text-decoration-none"
5252
style={{ textDecoration: 'none' }}
5353
>
5454
<div className="article-card h-full">
5555
{/* Category Badge */}
5656
<div className="card-category">{category}</div>
57-
57+
5858
{/* Card Image */}
5959
<div className="card-image">
6060
<img src={imageUrl} alt={title} />
6161
</div>
62-
62+
6363
{/* Card Content */}
6464
<div className="card-content">
6565
<h3 className="card-title">{title}</h3>
6666
<p className="card-description">{content}</p>
67-
67+
6868
{/* Card Meta */}
6969
<div className="card-meta">
7070
<div className="card-author">
@@ -73,7 +73,7 @@ const BlogCard = ({
7373
</div>
7474
<span className="card-read-time">5 min read</span>
7575
</div>
76-
76+
7777
{/* Read More Button */}
7878
<div className="card-read-more">
7979
Read Article →

src/components/discussions/DiscussionCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export default function DiscussionCard({
123123
}}
124124
/>
125125
) : null}
126-
<div
126+
<div
127127
className="author-avatar-fallback"
128128
style={{ display: discussion.author.avatar_url ? 'none' : 'flex' }}
129129
>

src/components/giscus.tsx

Lines changed: 102 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,92 @@
1+
// import React, { useEffect, useRef } from "react";
2+
3+
// const GiscusComments: React.FC = () => {
4+
// const ref = useRef<HTMLDivElement>(null);
5+
6+
// useEffect(() => {
7+
// if (!ref.current) return;
8+
// // Prevent duplicate script injection
9+
// if (ref.current.querySelector("iframe")) return;
10+
11+
// const script = document.createElement("script");
12+
// script.src = "https://giscus.app/client.js";
13+
// script.setAttribute("data-repo", "recodehive/Support");
14+
// script.setAttribute("data-repo-id", "R_kgDOL9urew");
15+
// script.setAttribute("data-category", "General");
16+
// script.setAttribute("data-category-id", "DIC_kwDOL9ure84Cqizj");
17+
// script.setAttribute("data-mapping", "og:title");
18+
// script.setAttribute("data-strict", "0");
19+
// script.setAttribute("data-reactions-enabled", "1");
20+
// script.setAttribute("data-emit-metadata", "0");
21+
// script.setAttribute("data-input-position", "top");
22+
// script.setAttribute("data-theme", "preferred_color_scheme");
23+
// script.setAttribute("data-lang", "en");
24+
// script.crossOrigin = "anonymous";
25+
// script.async = true;
26+
// ref.current.appendChild(script);
27+
// }, []);
28+
29+
// return <div ref={ref} />;
30+
// };
31+
32+
// export default GiscusComments;
33+
34+
// import React, { useEffect, useRef } from "react";
35+
// import { useColorMode } from "@docusaurus/theme-common"
36+
37+
// const GiscusComments: React.FC = () => {
38+
// const ref = useRef<HTMLDivElement>(null);
39+
// const { colorMode } = useColorMode();
40+
// console.log(colorMode)
41+
// useEffect(() => {
42+
// if (!ref.current) return;
43+
// // Prevent duplicate script injection
44+
// if (ref.current.querySelector("iframe")) return;
45+
46+
// const script = document.createElement("script");
47+
// script.src = "https://giscus.app/client.js";
48+
// script.setAttribute("data-repo", "recodehive/Support");
49+
// script.setAttribute("data-repo-id", "R_kgDOL9urew");
50+
// script.setAttribute("data-category", "General");
51+
// script.setAttribute("data-category-id", "DIC_kwDOL9ure84Cqizj");
52+
// script.setAttribute("data-mapping", "og:title");
53+
// script.setAttribute("data-strict", "0");
54+
// script.setAttribute("data-reactions-enabled", "1");
55+
// script.setAttribute("data-emit-metadata", "0");
56+
// script.setAttribute("data-input-position", "top");
57+
58+
// // Set the default theme to "light"
59+
// script.setAttribute("data-theme", colorMode);
60+
61+
// script.setAttribute("data-lang", "en");
62+
// script.crossOrigin = "anonymous";
63+
// script.async = true;
64+
// ref.current.appendChild(script);
65+
// }, [colorMode]);
66+
67+
// return <div ref={ref} />;
68+
// };
69+
70+
// export default GiscusComments;
71+
172
import React, { useEffect, useRef } from "react";
73+
import { useColorMode } from "@docusaurus/theme-common";
274

375
const GiscusComments: React.FC = () => {
476
const ref = useRef<HTMLDivElement>(null);
77+
const { colorMode } = useColorMode(); // colorMode is 'light' or 'dark'
578

79+
// 1. This useEffect handles the initial script loading ONCE.
680
useEffect(() => {
7-
if (!ref.current) return;
8-
// Prevent duplicate script injection
9-
if (ref.current.querySelector("iframe")) return;
81+
// Exit if the ref isn't set or if the script is already there
82+
if (!ref.current || ref.current.hasChildNodes()) {
83+
return;
84+
}
1085

1186
const script = document.createElement("script");
1287
script.src = "https://giscus.app/client.js";
88+
script.async = true;
89+
script.crossOrigin = "anonymous";
1390
script.setAttribute("data-repo", "recodehive/Support");
1491
script.setAttribute("data-repo-id", "R_kgDOL9urew");
1592
script.setAttribute("data-category", "General");
@@ -19,12 +96,30 @@ const GiscusComments: React.FC = () => {
1996
script.setAttribute("data-reactions-enabled", "1");
2097
script.setAttribute("data-emit-metadata", "0");
2198
script.setAttribute("data-input-position", "top");
22-
script.setAttribute("data-theme", "preferred_color_scheme");
2399
script.setAttribute("data-lang", "en");
24-
script.crossOrigin = "anonymous";
25-
script.async = true;
100+
101+
// Use the initial colorMode from Docusaurus for the initial theme
102+
script.setAttribute("data-theme", colorMode);
103+
26104
ref.current.appendChild(script);
27-
}, []);
105+
}, []); // <-- Empty dependency array ensures this runs only once on mount.
106+
107+
// 2. This useEffect watches for changes in colorMode and sends a message to Giscus.
108+
useEffect(() => {
109+
const iframe = ref.current?.querySelector<HTMLIFrameElement>(
110+
"iframe.giscus-frame"
111+
);
112+
113+
if (!iframe) {
114+
return;
115+
}
116+
117+
// Send a message to the Giscus iframe to update its theme
118+
iframe.contentWindow.postMessage(
119+
{ giscus: { setConfig: { theme: colorMode } } },
120+
"https://giscus.app"
121+
);
122+
}, [colorMode]); // <-- This runs every time colorMode changes.
28123

29124
return <div ref={ref} />;
30125
};

0 commit comments

Comments
 (0)