From 67e1beea7052ea515d47b881d4a30fa3ed847b0b Mon Sep 17 00:00:00 2001 From: Emile Bangma Date: Fri, 18 Oct 2024 00:30:16 +0200 Subject: [PATCH] feat(comments): support custom giscus themes (#1526) Co-authored-by: Aaron Pham Co-authored-by: Aaron Pham --- docs/features/comments.md | 33 +++++++ quartz/components/Comments.tsx | 8 ++ quartz/components/scripts/comments.inline.ts | 28 +++++- quartz/static/giscus/dark.css | 99 ++++++++++++++++++++ quartz/static/giscus/light.css | 99 ++++++++++++++++++++ 5 files changed, 265 insertions(+), 2 deletions(-) create mode 100644 quartz/static/giscus/dark.css create mode 100644 quartz/static/giscus/light.css diff --git a/docs/features/comments.md b/docs/features/comments.md index 92ea754..1f11eff 100644 --- a/docs/features/comments.md +++ b/docs/features/comments.md @@ -63,6 +63,18 @@ type Options = { category: string categoryId: string + // Url to folder with custom themes + // defaults to 'https://${cfg.baseUrl}/static/giscus' + themeUrl?: string + + // filename for light theme .css file + // defaults to 'light' + lightTheme?: string + + // filename for dark theme .css file + // defaults to 'dark' + darkTheme?: string + // how to map pages -> discussions // defaults to 'url' mapping?: "url" | "title" | "og:title" | "specific" | "number" | "pathname" @@ -81,3 +93,24 @@ type Options = { } } ``` + +#### Custom CSS theme + +Quartz supports custom theme for Giscus. To use a custom CSS theme, place the `.css` file inside the `quartz/static` folder and set the configuration values. + +For example, if you have a light theme `light-theme.css`, a dark theme `dark-theme.css`, and your Quartz site is hosted at `https://example.com/`: + +```ts +afterBody: [ + Component.Comments({ + provider: 'giscus', + options: { + // Other options + + themeUrl: "https://example.com/static/giscus", // corresponds to quartz/static/giscus/ + lightTheme: "light-theme", // corresponds to light-theme.css in quartz/static/giscus/ + darkTheme: "dark-theme", // corresponds to dark-theme.css quartz/static/giscus/ + } + }), +], +``` diff --git a/quartz/components/Comments.tsx b/quartz/components/Comments.tsx index 8e44940..44331cc 100644 --- a/quartz/components/Comments.tsx +++ b/quartz/components/Comments.tsx @@ -10,6 +10,9 @@ type Options = { repoId: string category: string categoryId: string + themeUrl?: string + lightTheme?: string + darkTheme?: string mapping?: "url" | "title" | "og:title" | "specific" | "number" | "pathname" strict?: boolean reactionsEnabled?: boolean @@ -34,6 +37,11 @@ export default ((opts: Options) => { data-strict={boolToStringBool(opts.options.strict ?? true)} data-reactions-enabled={boolToStringBool(opts.options.reactionsEnabled ?? true)} data-input-position={opts.options.inputPosition ?? "bottom"} + data-light-theme={opts.options.lightTheme ?? "light"} + data-dark-theme={opts.options.darkTheme ?? "dark"} + data-theme-url={ + opts.options.themeUrl ?? `https://${cfg.baseUrl ?? "example.com"}/static/giscus` + } > ) } diff --git a/quartz/components/scripts/comments.inline.ts b/quartz/components/scripts/comments.inline.ts index 4ab29f0..c54230f 100644 --- a/quartz/components/scripts/comments.inline.ts +++ b/quartz/components/scripts/comments.inline.ts @@ -13,7 +13,7 @@ const changeTheme = (e: CustomEventMap["themechange"]) => { { giscus: { setConfig: { - theme: theme, + theme: getThemeUrl(getThemeName(theme)), }, }, }, @@ -21,12 +21,36 @@ const changeTheme = (e: CustomEventMap["themechange"]) => { ) } +const getThemeName = (theme: string) => { + if (theme !== "dark" && theme !== "light") { + return theme + } + const giscusContainer = document.querySelector(".giscus") as GiscusElement + if (!giscusContainer) { + return theme + } + const darkGiscus = giscusContainer.dataset.darkTheme ?? "dark" + const lightGiscus = giscusContainer.dataset.lightTheme ?? "light" + return theme === "dark" ? darkGiscus : lightGiscus +} + +const getThemeUrl = (theme: string) => { + const giscusContainer = document.querySelector(".giscus") as GiscusElement + if (!giscusContainer) { + return `https://giscus.app/themes/${theme}.css` + } + return `${giscusContainer.dataset.themeUrl ?? "https://giscus.app/themes"}/${theme}.css` +} + type GiscusElement = Omit & { dataset: DOMStringMap & { repo: `${string}/${string}` repoId: string category: string categoryId: string + themeUrl: string + lightTheme: string + darkTheme: string mapping: "url" | "title" | "og:title" | "specific" | "number" | "pathname" strict: string reactionsEnabled: string @@ -57,7 +81,7 @@ document.addEventListener("nav", () => { const theme = document.documentElement.getAttribute("saved-theme") if (theme) { - giscusScript.setAttribute("data-theme", theme) + giscusScript.setAttribute("data-theme", getThemeUrl(getThemeName(theme))) } giscusContainer.appendChild(giscusScript) diff --git a/quartz/static/giscus/dark.css b/quartz/static/giscus/dark.css new file mode 100644 index 0000000..e98088f --- /dev/null +++ b/quartz/static/giscus/dark.css @@ -0,0 +1,99 @@ +/*! MIT License + * Copyright (c) 2018 GitHub Inc. + * https://github.com/primer/primitives/blob/main/LICENSE + */ + +main { + --color-prettylights-syntax-comment: #8b949e; + --color-prettylights-syntax-constant: #79c0ff; + --color-prettylights-syntax-entity: #d2a8ff; + --color-prettylights-syntax-storage-modifier-import: #c9d1d9; + --color-prettylights-syntax-entity-tag: #7ee787; + --color-prettylights-syntax-keyword: #ff7b72; + --color-prettylights-syntax-string: #a5d6ff; + --color-prettylights-syntax-variable: #ffa657; + --color-prettylights-syntax-brackethighlighter-unmatched: #f85149; + --color-prettylights-syntax-invalid-illegal-text: #f0f6fc; + --color-prettylights-syntax-invalid-illegal-bg: #8e1519; + --color-prettylights-syntax-carriage-return-text: #f0f6fc; + --color-prettylights-syntax-carriage-return-bg: #b62324; + --color-prettylights-syntax-string-regexp: #7ee787; + --color-prettylights-syntax-markup-list: #f2cc60; + --color-prettylights-syntax-markup-heading: #1f6feb; + --color-prettylights-syntax-markup-italic: #c9d1d9; + --color-prettylights-syntax-markup-bold: #c9d1d9; + --color-prettylights-syntax-markup-deleted-text: #ffdcd7; + --color-prettylights-syntax-markup-deleted-bg: #67060c; + --color-prettylights-syntax-markup-inserted-text: #aff5b4; + --color-prettylights-syntax-markup-inserted-bg: #033a16; + --color-prettylights-syntax-markup-changed-text: #ffdfb6; + --color-prettylights-syntax-markup-changed-bg: #5a1e02; + --color-prettylights-syntax-markup-ignored-text: #c9d1d9; + --color-prettylights-syntax-markup-ignored-bg: #1158c7; + --color-prettylights-syntax-meta-diff-range: #d2a8ff; + --color-prettylights-syntax-brackethighlighter-angle: #8b949e; + --color-prettylights-syntax-sublimelinter-gutter-mark: #484f58; + --color-prettylights-syntax-constant-other-reference-link: #a5d6ff; + --color-btn-text: #d4d4d4; /* --darkgray */ + --color-btn-bg: #161618; /* --light */ + --color-btn-border: rgb(240, 246, 252 / 10%); /* --dark */ + --color-btn-shadow: 0 0 transparent; + --color-btn-inset-shadow: 0 0 transparent; + --color-btn-hover-bg: #30363d; + --color-btn-hover-border: #8b949e; + --color-btn-active-bg: hsl(212deg 12% 18% / 100%); + --color-btn-active-border: #6e7681; + --color-btn-selected-bg: #161b22; + --color-btn-primary-text: #fff; + --color-btn-primary-bg: #84a59d; /* --tertiary */ + --color-btn-primary-border: rgb(240, 246, 252 / 10%); /* --dark */ + --color-btn-primary-shadow: 0 0 transparent; + --color-btn-primary-inset-shadow: 0 0 transparent; + --color-btn-primary-hover-bg: #7b97aa; /* --secondary */ + --color-btn-primary-hover-border: rgb(240, 246, 252 / 10%); /* --dark */ + --color-btn-primary-selected-bg: #7b97aa; /* --secondary */ + --color-btn-primary-selected-shadow: 0 0 transparent; + --color-btn-primary-disabled-text: rgba(33, 32, 32, 0.5); + --color-btn-primary-disabled-bg: rgb(35 134 54 / 60%); + --color-btn-primary-disabled-border: rgb(240 246 252 / 10%); + --color-action-list-item-default-hover-bg: rgb(177 186 196 / 12%); + --color-segmented-control-bg: rgb(110 118 129 / 10%); + --color-segmented-control-button-bg: #0d1117; + --color-segmented-control-button-selected-border: #6e7681; + --color-fg-default: #ebebec; /* --dark */ + --color-fg-muted: #d4d4d4; /* --darkgray */ + --color-fg-subtle: #d4d4d4; /* --darkgray */ + --color-canvas-default: #0d1117; + --color-canvas-overlay: #161b22; + --color-canvas-inset: #010409; + --color-canvas-subtle: #161b22; + --color-border-default: #30363d; + --color-border-muted: #21262d; + --color-neutral-muted: rgb(110 118 129 / 40%); + --color-accent-fg: #2f81f7; + --color-accent-emphasis: #1f6feb; + --color-accent-muted: rgb(56 139 253 / 40%); + --color-accent-subtle: rgb(56 139 253 / 10%); + --color-success-fg: #3fb950; + --color-attention-fg: #d29922; + --color-attention-muted: rgb(187 128 9 / 40%); + --color-attention-subtle: rgb(187 128 9 / 15%); + --color-danger-fg: #f85149; + --color-danger-muted: rgb(248 81 73 / 40%); + --color-danger-subtle: rgb(248 81 73 / 10%); + --color-primer-shadow-inset: 0 0 transparent; + --color-scale-gray-7: #21262d; + --color-scale-blue-8: #0c2d6b; + + /*! Extensions from @primer/css/alerts/flash.scss */ + --color-social-reaction-bg-hover: var(--color-scale-gray-7); + --color-social-reaction-bg-reacted-hover: var(--color-scale-blue-8); +} + +main .pagination-loader-container { + background-image: url("https://github.com/images/modules/pulls/progressive-disclosure-line-dark.svg"); +} + +main .gsc-loading-image { + background-image: url("https://github.githubassets.com/images/mona-loading-dark.gif"); +} diff --git a/quartz/static/giscus/light.css b/quartz/static/giscus/light.css new file mode 100644 index 0000000..84b58c0 --- /dev/null +++ b/quartz/static/giscus/light.css @@ -0,0 +1,99 @@ +/*! MIT License + * Copyright (c) 2018 GitHub Inc. + * https://github.com/primer/primitives/blob/main/LICENSE + */ + +main { + --color-prettylights-syntax-comment: #6e7781; + --color-prettylights-syntax-constant: #0550ae; + --color-prettylights-syntax-entity: #8250df; + --color-prettylights-syntax-storage-modifier-import: #24292f; + --color-prettylights-syntax-entity-tag: #116329; + --color-prettylights-syntax-keyword: #cf222e; + --color-prettylights-syntax-string: #0a3069; + --color-prettylights-syntax-variable: #953800; + --color-prettylights-syntax-brackethighlighter-unmatched: #82071e; + --color-prettylights-syntax-invalid-illegal-text: #f6f8fa; + --color-prettylights-syntax-invalid-illegal-bg: #82071e; + --color-prettylights-syntax-carriage-return-text: #f6f8fa; + --color-prettylights-syntax-carriage-return-bg: #cf222e; + --color-prettylights-syntax-string-regexp: #116329; + --color-prettylights-syntax-markup-list: #3b2300; + --color-prettylights-syntax-markup-heading: #0550ae; + --color-prettylights-syntax-markup-italic: #24292f; + --color-prettylights-syntax-markup-bold: #24292f; + --color-prettylights-syntax-markup-deleted-text: #82071e; + --color-prettylights-syntax-markup-deleted-bg: #ffebe9; + --color-prettylights-syntax-markup-inserted-text: #116329; + --color-prettylights-syntax-markup-inserted-bg: #dafbe1; + --color-prettylights-syntax-markup-changed-text: #953800; + --color-prettylights-syntax-markup-changed-bg: #ffd8b5; + --color-prettylights-syntax-markup-ignored-text: #eaeef2; + --color-prettylights-syntax-markup-ignored-bg: #0550ae; + --color-prettylights-syntax-meta-diff-range: #8250df; + --color-prettylights-syntax-brackethighlighter-angle: #57606a; + --color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f; + --color-prettylights-syntax-constant-other-reference-link: #0a3069; + --color-btn-text: #4e4e4e; /* --darkgray */ + --color-btn-bg: #faf8f8; /* --light */ + --color-btn-border: rgb(43, 43, 43 / 15%); /* --dark */ + --color-btn-shadow: 0 1px 0 rgb(31 35 40 / 4%); + --color-btn-inset-shadow: inset 0 1px 0 rgb(255 255 255 / 25%); + --color-btn-hover-bg: #f3f4f6; + --color-btn-hover-border: rgb(43, 43, 43 / 15%); /* --dark */ + --color-btn-active-bg: hsl(220deg 14% 93% / 100%); + --color-btn-active-border: rgb(31 35 40 / 15%); + --color-btn-selected-bg: hsl(220deg 14% 94% / 100%); + --color-btn-primary-text: #fff; + --color-btn-primary-bg: #84a59d; /* --tertiary */ + --color-btn-primary-border: rgb(43, 43, 43 / 15%); /* --dark */ + --color-btn-primary-shadow: 0 1px 0 rgb(31 35 40 / 10%); + --color-btn-primary-inset-shadow: inset 0 1px 0 rgb(255 255 255 / 3%); + --color-btn-primary-hover-bg: #284b63; /* --secondary */ + --color-btn-primary-hover-border: rgb(43, 43, 43 / 15%); /* --dark */ + --color-btn-primary-selected-bg: #284b63; /* --secondary */ + --color-btn-primary-selected-shadow: inset 0 1px 0 rgb(0 45 17 / 20%); + --color-btn-primary-disabled-text: rgb(255 255 255 / 80%); + --color-btn-primary-disabled-bg: #94d3a2; + --color-btn-primary-disabled-border: rgb(31 35 40 / 15%); + --color-action-list-item-default-hover-bg: rgb(208 215 222 / 32%); + --color-segmented-control-bg: #eaeef2; + --color-segmented-control-button-bg: #fff; + --color-segmented-control-button-selected-border: #8c959f; + --color-fg-default: #2b2b2b; /* --dark */ + --color-fg-muted: #4e4e4e; /* --darkgray */ + --color-fg-subtle: #4e4e4e; /* --darkgray */ + --color-canvas-default: #fff; + --color-canvas-overlay: #fff; + --color-canvas-inset: #f6f8fa; + --color-canvas-subtle: #f6f8fa; + --color-border-default: #d0d7de; + --color-border-muted: hsl(210deg 18% 87% / 100%); + --color-neutral-muted: rgb(175 184 193 / 20%); + --color-accent-fg: #0969da; + --color-accent-emphasis: #0969da; + --color-accent-muted: rgb(84 174 255 / 40%); + --color-accent-subtle: #ddf4ff; + --color-success-fg: #1a7f37; + --color-attention-fg: #9a6700; + --color-attention-muted: rgb(212 167 44 / 40%); + --color-attention-subtle: #fff8c5; + --color-danger-fg: #d1242f; + --color-danger-muted: rgb(255 129 130 / 40%); + --color-danger-subtle: #ffebe9; + --color-primer-shadow-inset: inset 0 1px 0 rgb(208 215 222 / 20%); + --color-scale-gray-1: #eaeef2; + --color-scale-blue-1: #b6e3ff; + + /*! Extensions from @primer/css/alerts/flash.scss */ + --color-social-reaction-bg-hover: var(--color-scale-gray-1); + --color-social-reaction-bg-reacted-hover: var(--color-scale-blue-1); +} + +main .pagination-loader-container { + background-image: url("https://github.com/images/modules/pulls/progressive-disclosure-line.svg"); +} + +main .gsc-loading-image { + background-image: url("https://github.githubassets.com/images/mona-loading-default.gif"); +}