在 windicss 中,提供了媒體查詢和 class 兩種方式實現暗黑模式適配。而且比較省心的是 —— 其提供的 dark
變體會自動根據選擇的適配模式,生成對應的代碼,可以有效避免寫出一堆沒用的 css,看起來也比較清晰。
為了方便控制,我們選擇使用 class 的方式來切換暗黑模式(即給根元素賦予類名 dark
來切換到暗黑模式)
基礎樣式#
首先,需要一些全局 css 來解決 windicss 無法覆蓋的樣式。
滾動條顏色改變#
正常情況下,你可能會想用 -webkit-scrollbar
偽類,但是,其實有更優雅的寫法。瀏覽器提供了一個 color-scheme
css 屬性,將其設置為 dark,瀏覽器便會自動將頁面內所有瀏覽器自帶的元素渲染成暗色風格
html.dark {
color-scheme: dark;
}
圖片亮度降低#
也很簡單,應用一個 filter 就好了
html.dark img {
filter: brightness(0.8);
}
自動檢測#
接下來就是重頭戲了,如何判斷並給 html 元素加上dark
類名,毕竟 windicss 可不會幫我們自動處理。
我們會在前端為用戶提供一個下拉框,用戶可以選擇自動適應、保持暗黑模式、保持明亮模式
為了避免頁面初載入時樣式切換導致的閃屏,最終決定將該配置儲存到 cookie 而非 localstorage 中,這樣能夠發揮 ssr 的作用,當用戶強制暗黑 / 明亮時,服務器就能將類名寫入 html 標籤中。
function readDarkModeInStorage() {
const darkMode = useCookie('darkMode')
const possibleValues = ['auto', 'dark', 'light']
if (darkMode.value && possibleValues.includes(darkMode.value)) {
return darkMode.value
} else {
return 'auto'
}
}
上面是一個輔助函數,用於從儲存中讀出暗黑模式配置(服務器 / 客戶端均可用)
function setModeClass(isDark: boolean): void {
if (isDark) {
useHead({
htmlAttrs: { class: 'dark' },
meta: [{ name: 'theme-color', content: '#121212' }],
})
} else {
useHead({
htmlAttrs: { class: '' },
meta: [{ name: 'theme-color', content: '#ffffff' }],
})
}
}
一個用於設置暗黑模式樣式的工具函數,當傳入布爾值時,會同時設置 html 的類名和 theme-color 的 meta 標籤(ssr/csr 均可用)
使用了來自 VueUse 的 useHead 方法
const currentMode = ref(readDarkModeInStorage())
const preferredDark = usePreferredDark()
watchEffect(() => {
if (currentMode.value === 'auto') {
if (preferredDark.value) {
setModeClass(true)
} else {
setModeClass(false)
}
} else if (currentMode.value === 'dark') {
setModeClass(true)
} else if (currentMode.value === 'light') {
setModeClass(false)
}
useCookie('darkMode').value = currentMode.value
})
這裡便是關鍵,首先,讀入配置並初始化到 currentMode
變量,接著,使用 VueUse 提供的 usePreferredDark
來獲取當前瀏覽器的顏色模式。
使用一個監聽副作用的函數,當上面兩個值發生改變時,調用 setModeClass
工具函數去完成最終的類名修改,並將配置寫入 cookie。