iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0
Vue.js

用 Nuxt Content 搭配 Obsidian 建立自己的 Digital Garden系列 第 25

增加關燈模式,讓閱讀更加護眼

  • 分享至 

  • xImage
  •  

截至目前為止,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 事件就大功告成啦!

完成示意圖:


上一篇
新增其他發文類型
下一篇
Icon:增加圖示讓視覺更多元
系列文
用 Nuxt Content 搭配 Obsidian 建立自己的 Digital Garden30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言