截至目前為止,Digital Garden 的基礎功能都大致完成了,唯一美中不足的就是習慣 Dark Mode 的情況下,閱讀起來頗為刺眼,尤其是晚上時,更加不舒服.所以今天就來實作 Dark Mode 吧!
在開始前,我先針對目前的程式碼做了些重構,讓實作關燈模式的成本降低。我將有用到 .prose
的地方集中在 @/layouts/default.vue
,這邊原本就有設置了,其他地方有此修飾就不太有意義。並且將原本 .text-gray-xxx
和 .text-slate-xxx
的地方都先移除,因為之前並沒有一致的設計規範,先透過這個方式讓顏色先統一,日後要再顯現不一樣是在做修改。
重構完後,會先分兩部分實作關燈功能,分別是跟隨作業系統本身去切換顏色,再來在網站上實作開關。
由於前面都重構了,要實作就顯得簡單很多。首先先編輯 @/app.vue
,並加入下方程式碼:
<script setup lang="ts">
useHead({
bodyAttrs: {
class:
'bg-white text-gray-700 dark:bg-neutral-900 dark:text-gray-300',
},
})
</script>
這是利用 Tailwind 內建的 variant,只要使用 dark:
作為樣式的前綴,就會是 Darkmode 時啟用。
這邊則是使用 useHead()
這個 API,並透過旗下的 bodyAttrs.class
屬性,讓這些 CSS class 可以套用在 <body>
中。
完成後預覽目前的狀態,發現中間內容區仍然是黑色的,這是因為這邊被 .prose
給覆寫了,所以我們也要為其加上關燈模式的配色。
編輯 @/layouts/dafault.vue
,並在原本有使用 .prose
的標籤中,加入 dark:prose-invert
即可。
這樣關燈模式就實作完成:
要實作開關,我們需要另一個 node.js 套件 @nuxtjs/color-mode
的輔助,首先安裝:
$ pnpm install --save-dev @nuxtjs/color-mode
並將其加入 @/nuxt.config.ts
中的設定中,並參照其文件,將 colorMode.classSuffix
設置為空值
export default defineNuxtConfig({
modules: [
'@nuxt/content',
'@nuxtjs/tailwindcss',
'@nuxtjs/color-mode', // Add this line
],
colorMode: {
classSuffix: '',
},
})
並編輯 @/tailwind.config.ts
,加入 darkMode: 'class'
:
export default <Partial<Config>>{
plugins: [require('@tailwindcss/typography')],
darkMode: 'class',
}
接著編輯 @/components/TheHeader.vue
,加入等等要建立的 <ThemeToggle />
:
<div class="flex items-center text-base leading-5">
<nav class="hidden sm:block">
<!-- NuxtLink.... -->
</nav>
<ThemeToggle />
</div>
最後新增 @/components/ThemeToggle.vue
並輸入以下程式碼:
<template>
<button
aria-label="Toggle Dark Mode"
className="ml-1 mr-1 h-8 w-8 rounded p-1 sm:ml-4"
:onClick="() => toggle()"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
className="text-gray-900 dark:text-gray-100"
>
<path
v-show="colorMode.value === 'dark'"
fillRule="evenodd"
d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"
clipRule="evenodd"
/>
<path
v-show="colorMode.value === 'light'"
d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"
/>
</svg>
</button>
</template>
<script setup lang="ts">
const colorMode = useColorMode()
const toggle = () => {
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
}
</script>
這裡分別使用 <svg />
的方式建立起太陽與月亮的圖示,並且在 <path />
設定顯示的時機點。
接著透過 useColorMode()
取得 colorMode 的實體,並透過 toggle()
方法進行切換,最後在綁定在圖示的 onClick
事件就大功告成啦!
完成示意圖: