使用 Gatsby.js 搭建靜態(tài)博客黑暗模式

來源 | https://ssshooter.com/2020-06-25-gatsby-blog-8/
方案 1
@media screen and (prefers-color-scheme: dark) {:root {--linkColor: #ec9bab;--fontColor: #ecc3cb;--codeBlock: #3c495b;--code: #c3c7cb;--divider: #3e4b5e;}}@media screen and (prefers-color-scheme: light) {:root {--linkColor: #683741;--fontColor: hsl(284, 20%, 30%);--codeBlock: #3c495b;--code: #c3c7cb;--divider: #eee;}}body {background-color: var(--backgroundColor);color: var(--fontColor);transition: all 0.5s ease-in-out;}
(prism 用戶還要自己整理一下兩個模式的 prism 樣式)
css 變量除了在 IE 不能用之外其他瀏覽器的適應性還算不錯,如果必須適配 IE 的話需要另外寫一份不用變量的樣式保底。
方案 2
按上面的寫法其實已經(jīng)可以實現(xiàn)根據(jù)系統(tǒng)配置轉(zhuǎn)換明亮和黑暗模式,但是要做到直接在網(wǎng)頁通過一個切換按鈕手動修改還是遠遠不夠,方案 2 登場。
因為現(xiàn)在 JavaScript 不能改變系統(tǒng)級的明暗模式,只能獲取和監(jiān)聽模式變化,所以手動操作鐵定不能靠 prefers-color-scheme 了。
我們需要反過來通過?JavaScript?獲取 color-scheme 然后告訴 CSS 明暗模式發(fā)生變化,這樣的變化包括系統(tǒng)級的改變和用戶在網(wǎng)頁選擇改變。
首先把兩組變量的作用范圍改為 class:
.light-theme {--linkColor: #ec9bab;--fontColor: #ecc3cb;--codeBlock: #3c495b;--code: #c3c7cb;--divider: #3e4b5e;}.dark-theme {--linkColor: #683741;--fontColor: hsl(284, 20%, 30%);--codeBlock: #3c495b;--code: #c3c7cb;--divider: #eee;}
下面這一段代碼我寫在 Layout.js 的 componentDidMount 中,只有這里才是服務器渲染的范圍之外。
// setTheme 的實現(xiàn)setTheme = themeName => {localStorage.setItem('theme', themeName)document.documentElement.className = themeName + '-theme'this.setState({theme: themeName,})}// 第一部分let localTheme = localStorage.getItem('theme')if (localTheme) {if (localTheme === 'dark') {this.setTheme('dark')} else {this.setTheme('light')}} else if (window.matchMedia) {if (window.matchMedia('(prefers-color-scheme: dark)').matches) {this.setTheme('dark')} else {this.setTheme('light')}} else {// default lightthis.setTheme('light')}// 第二部分const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')darkModeMediaQuery.addListener(e => {const darkModeOn = e.matchesif (darkModeOn) {this.setTheme('dark')} else {this.setTheme('light')}})
先講 setTheme 的實現(xiàn):
數(shù)據(jù)持久化
實現(xiàn)設置整個文檔的 class,然后 CSS 根據(jù) class 配置變量(而不是方案 1 的媒體查詢)
修改組件狀態(tài)
第一部分是頁面初始化時處理頁面主題的邏輯:
首先查詢有無被保存到 localstorage 的主題,有的話直接使用
沒有的話使用 JavaScript 查詢系統(tǒng)明暗方案,然后按系統(tǒng)配置設定
上面兩種都不可行的情況下默認使用 light
第二部分是 JavaScript 監(jiān)聽系統(tǒng)明暗方案修改,響應式地改變頁面的明暗方案(意味著不需要刷新頁面)
剩下的切換按鈕就沒什么特別的,所以不放代碼了。
hack
最后附帶一個處理畫面閃爍的方法。
雖然已經(jīng)在樣式添加 transition 屬性,但是從 JavaScript 程序知道用戶需要的明暗模式前仍然有一段空擋。如果用戶選擇了黑暗模式,就會造成畫面先是明亮模式,然后幾百毫秒后變?yōu)楹诎的J降膶擂尉置妗?/span>
我的解決方案是先把頁面 display 設為 none,在判斷并設置完頁面 class 之后再將頁面設為可見,這樣就避免了畫面閃爍,但是頁面展示時間會慢一點點點點。
document.documentElement.style.display = 'none'// 同步的判斷程序document.documentElement.style.display = 'block'

