|
| 1 | ++++ |
| 2 | +title = "Hugo评论系统自适应博客主题: 支持dark与light theme" |
| 3 | +date = 2024-12-04T13:25:00+08:00 |
| 4 | +lastmod = 2024-12-04T14:27:41+08:00 |
| 5 | +tags = ["blog", "hugo"] |
| 6 | +categories = ["blog", "hugo"] |
| 7 | +draft = false |
| 8 | +toc = true |
| 9 | ++++ |
| 10 | + |
| 11 | +## <span class="section-num">1</span> 问题 {#问题} |
| 12 | + |
| 13 | +评论系统是博客的关键组件,Hugo 支持若干个评论系统,包括流行的 [Disqus](https://disqus.com/), 基于 GitHub 的 [Giscus](https://giscus.app) 和 [Utteranc](https://utteranc.es/), 以及其他[评论系统](https://gohugo.io/content-management/comments/) |
| 14 | + |
| 15 | +我[博客](https://ramsayleung.github.io/)使用的评论系统是 Utteranc, 主题是 [PaperMod](https://github.com/adityatelange/hugo-PaperMod/), PaperMod 支持 dark 和 light 两种主题, 在初始化 Utteranc 时可以指定 theme, 如 `Github-Light` |
| 16 | + |
| 17 | +```html |
| 18 | +<script src="https://utteranc.es/client.js" |
| 19 | + repo="ramsayleung/comment" |
| 20 | + issue-term="title" |
| 21 | + theme="github-light" |
| 22 | + crossorigin="anonymous" |
| 23 | + async> |
| 24 | +</script> |
| 25 | +``` |
| 26 | + |
| 27 | +但是 theme 在初始化时就指定好了,那么在博客切换到 dark theme 的时候, Utteranc 也不会自适应 dark theme,博客的theme与评论theme就不一致: |
| 28 | + |
| 29 | +{{< figure src="/ox-hugo/responsive_comment_theme.jpg" >}} |
| 30 | + |
| 31 | + |
| 32 | +## <span class="section-num">2</span> 解决思路 {#解决思路} |
| 33 | + |
| 34 | +解决思路其实很简单,就获取当前的 theme, 然后再初始化 Utteranc 对应的 theme; 再在用户切换 theme 之后,再重新初始化 `Utteranc`. |
| 35 | + |
| 36 | + |
| 37 | +### <span class="section-num">2.1</span> 获取当前Theme {#获取当前theme} |
| 38 | + |
| 39 | +Hugo 并没有提供标准接口来获取当前主题, 虽然可以通过以下的方式来获取的 theme: |
| 40 | + |
| 41 | +```js |
| 42 | +const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; |
| 43 | +console.log(isDarkMode ? 'dark' : 'light'); |
| 44 | +``` |
| 45 | + |
| 46 | +但是这个是通过编译出来的CSS来判断当前theme,用户一旦手动切换了 theme, 上面的代码就无法生效了. |
| 47 | + |
| 48 | +每个Hugo主题定义的方式可能还不一样, 以 PaperMod 为例,观察之后发现,light theme的时候body的html 为: |
| 49 | + |
| 50 | +```css |
| 51 | +<body id = "top" class=""> |
| 52 | +</body> |
| 53 | +``` |
| 54 | + |
| 55 | +dark theme 的时候 `class` 就变为了 `class`"dark"=: |
| 56 | + |
| 57 | +```css |
| 58 | +<body id = "top" class=""> |
| 59 | +</body> |
| 60 | +``` |
| 61 | + |
| 62 | +所以可以通过判断 `body` 是否包含 `dark` 的 class 来判断当前是否为 dark theme. |
| 63 | + |
| 64 | +```js |
| 65 | +// 不同的Hugo theme可能会需要不同的判断方式 |
| 66 | +function getCurrentTheme() { |
| 67 | + return document.body.classList.contains('dark') ? 'dark' : 'light'; |
| 68 | +} |
| 69 | +``` |
| 70 | + |
| 71 | + |
| 72 | +### <span class="section-num">2.2</span> 初始化评论系统 {#初始化评论系统} |
| 73 | + |
| 74 | +[Utteranc](https://utteranc.es/) 文档提供的启用评价系统代码如下: |
| 75 | + |
| 76 | +```html |
| 77 | +<script src="https://utteranc.es/client.js" |
| 78 | + repo="[ENTER REPO HERE]" |
| 79 | + issue-term="pathname" |
| 80 | + theme="github-light" |
| 81 | + crossorigin="anonymous" |
| 82 | + async> |
| 83 | +</script> |
| 84 | +``` |
| 85 | + |
| 86 | +因为我们需要在切换 Theme 时重新加载 Utteranc, 所以就需要通过 Javascript 来实现上面的HTML功能: |
| 87 | + |
| 88 | +```js |
| 89 | +function loadUtterances(darkMode=false) { |
| 90 | + const commentContainer = document.getElementById("comments-utteranc"); |
| 91 | + if (commentContainer !== null) { |
| 92 | + commentContainer.innerHTML = '' |
| 93 | + const commentScript = document.createElement("script"); |
| 94 | + commentScript.setAttribute("id", "utteranc"); |
| 95 | + commentScript.setAttribute("src", "https://utteranc.es/client.js"); |
| 96 | + commentScript.setAttribute("data-repo", "ramsayleung/comment"); |
| 97 | + commentScript.setAttribute("data-theme", darkMode ? "github-dark" : "github-light"); |
| 98 | + commentScript.setAttribute("data-issue-term", "title"); |
| 99 | + commentScript.setAttribute("crossorigin", "anonymous"); |
| 100 | + commentScript.setAttribute("async", "true"); |
| 101 | + commentContainer.appendChild(commentScript); |
| 102 | + } |
| 103 | +} |
| 104 | +``` |
| 105 | + |
| 106 | + |
| 107 | +### <span class="section-num">2.3</span> 监听主题变动 {#监听主题变动} |
| 108 | + |
| 109 | +用户可以在博客界面手动选择他们喜欢的主题,可以从 `dark` -> `light`, `light` -> `dark`, 我们需要做的就是监听主题的变动,在切换主题之后,重新加载评论系统。 |
| 110 | + |
| 111 | +```js |
| 112 | +// Watch for theme changes |
| 113 | +const themeObserver = new MutationObserver((mutations) => { |
| 114 | + mutations.forEach((mutation) => { |
| 115 | + if (mutation.type === 'attributes' && mutation.attributeName === 'class') { |
| 116 | + const isDarkMode = getCurrentTheme() === 'dark'; |
| 117 | + loadUtterances(isDarkMode); |
| 118 | + console.log(`changing theme`); |
| 119 | + } |
| 120 | + }); |
| 121 | +}); |
| 122 | + |
| 123 | +// Start observing the body element for class changes |
| 124 | +themeObserver.observe(document.body, { |
| 125 | + attributes: true, |
| 126 | + attributeFilter: ['class'] |
| 127 | +}); |
| 128 | +``` |
| 129 | + |
| 130 | + |
| 131 | +## <span class="section-num">3</span> 总结 {#总结} |
| 132 | + |
| 133 | +自适应评论系统的代码[在这里](https://github.com/ramsayleung/ramsayleung.github.io/blob/master/layouts/partials/comments.html), 实现的效果如下: |
| 134 | + |
| 135 | +{{< figure src="/ox-hugo/responsive_comment_theme_2.jpg" >}} |
| 136 | + |
| 137 | +{{< figure src="/ox-hugo/responsive_comment_theme_3.jpg" >}} |
0 commit comments