Phần 2/2 · Tùy chỉnh Giscus cho site của bạn

Tùy chỉnh theme Giscus cho khớp với site

Cách tạo theme CSS tùy chỉnh cho Giscus để phần comment khớp với color palette của site, thay vì trông như một widget GitHub bị nhét vào.

Đăng ngày

2 phút đọc

Vấn đề

Trong bài trước, mình đã viết cách giữ Giscus đồng bộ với nút dark mode của site. Nhưng đã switch giữa light và dark_dimmed, Giscus vẫn mang palette kiểu GitHub của riêng nó. Màu xanh, xám, và style nút không khớp tone của blog này. Widget trông như một trang GitHub bị nhúng vào, chứ không phải một phần tự nhiên của site.

Giải pháp

Ý tưởng rất đơn giản: thay theme Giscus có sẵn bằng file CSS tùy chỉnh, sau đó host trên domain của bạn, rồi trỏ widget vào những file đó thay bằng cách ép Giscus nhận một URL đầy đủ trong attribute data-theme. Nếu đưa vào URL kết thúc bằng .css, Gicus sẽ load stylesheet đó bên trong iframe.

Implementation

Lưu ý

  • Các snippet bên dưới chỉ giữ phần liên quan đến theming.

Bước 1: Lấy các biến Giscus mặc định

Giscus có các bộ CSS custom property dựa vào theme của nó. Điểm bắt đầu dễ nhất là lấy theme mặc định trực tiếp từ Giscus rồi dùng làm base:

Lưu vào local rồi bắt đầu chỉnh.

Các property chia thành vài nhóm:

  • Syntax highlighting — --color-prettylights-syntax-*
  • Buttons — --color-btn-*
  • Canvas và foreground — --color-canvas-*, --color-fg-*
  • Borders — --color-border-*
  • Accent — --color-accent-*

Bước 2: Map color palette của site lên các biến

Mình sẽ thay các biến màu bằng giá trị khớp với site:

/* public/giscus/light.css (excerpt) */
main {
--color-fg-default: #261813;
--color-fg-muted: #7d6a61;
--color-canvas-default: #fffaf4;
--color-canvas-inset: #f5e7df;
--color-border-default: #e9d7ce;
--color-accent-fg: #ce6141;
--color-accent-emphasis: #ce6141;
--color-btn-text: #261813;
--color-btn-bg: #f5e7df;
--color-btn-border: #e9d7ce;
--color-btn-primary-bg: #ce6141;
--color-btn-primary-border: #ce6141;
/* ... rest of the variables */
}

Variant dark cùng cấu trúc:

/* public/giscus/dark.css (excerpt) */
main {
--color-fg-default: #f5e9df;
--color-fg-muted: #a89082;
--color-canvas-default: #14100e;
--color-canvas-inset: #2b1f1b;
--color-border-default: #4a352f;
--color-accent-fg: #f0906d;
--color-accent-emphasis: #df7b5c;
--color-btn-text: #f5e9df;
--color-btn-bg: #2b1f1b;
--color-btn-border: #4a352f;
--color-btn-primary-bg: #df7b5c;
--color-btn-primary-border: #df7b5c;
/* ... rest of the variables */
}

Bước 3: Host file và trỏ Giscus vào chúng

Đặt cả hai file trong thư mục public (ví dụ public/giscus/light.css và public/giscus/dark.css). Trong Astro, mọi thứ dưới public/ được serve ở root của site, nên chúng sẽ có thể acess public từ /giscus/light.css và /giscus/dark.css.

Khi khởi tạo script Giscus, truyền URL đầy đủ thay vì tên theme:

const mode = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
script.setAttribute('data-theme', `${location.origin}/giscus/${mode}.css`);

Khi switch theme lúc runtime, mình cũng sẽ làm tương tự:

function currentGiscusTheme() {
const mode = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
return `${location.origin}/giscus/${mode}.css`;
}

Thông qua cách này, Giscus sẽ fetch CSS từ domain của bạn rồi apply bên trong iframe

Bước 4: Xử lý CORS

Vì iframe Giscus trên giscus.app sẽ fetch CSS từ domain của bạn, nên nó cần cho phép cross-origin request. Nếu không chỉnh header đúng, thì iframe sẽ fail 1 cách âm thầm

Development Environment (Netlify)

Mình thêm một header rule trong netlify.toml:

[[headers]]
for = "/giscus/*"
[headers.values]
Access-Control-Allow-Origin = "*"
Cache-Control = "public, max-age=31536000, immutable"

Local Environment (Astro + Vite)

Local thì dev server của Astro cũng cần làm tương tự, vì nếu không làm thì custom css cho Giscus chỉ chạy được trên Development Environment. Để configure được cho local, mình cần phải làm hai việc trong astro.config.mjs:

Thứ nhất, allowlist origin của Giscus để security middleware của Astro không chặn request:

security: {
allowedDomains: [{ hostname: 'giscus.app', protocol: 'https' }]
},

Thứ hai, thêm một Vite plugin nhỏ để set header CORS trên dev server:

vite: {
plugins: [
{
name: 'giscus-cors',
configureServer(server) {
server.middlewares.use((req, res, next) => {
if (req.url?.startsWith('/giscus/')) {
res.setHeader('Access-Control-Allow-Origin', '*');
}
next();
});
}
}
]
}

Kết luận

Sau khi thực hiện các bước trên, phần comment Giscus giờ đã khớp với site. Điểm mấu chốt ở đây là mình cần bắt Giscus nhận URL stylesheet đầy đủ trong data-theme.

Chia sẻ

Bạn đã hoàn thành Tùy chỉnh Giscus cho site của bạn. Về trang Series bài viết

Đọc tiếp

Về tác giả

Dương Lê
Kỹ sư phần mềm

Dương Lê là kỹ sư phần mềm với hơn sáu năm kinh nghiệm xây dựng và duy trì các ứng dụng web, từ những sản phẩm giai đoạn đầu đến các hệ thống đang vận hành trong môi trường thực tế.

Anh ấy có bằng Thạc sĩ Khoa học Máy tính và từng có nghiên cứu được công bố tại hai hội nghị học thuật (CITA 2023, IMCOM 2019).