Cover Image for Tailwind CSS でダークモードを実装してみた

Tailwind CSS でダークモードを実装してみた

NEXT.js
SvelteKit
Web草紙
Tailwind CSS

はじめに

パソコンのディスプレイを長時間見ていると目が疲れます。背景色が白で文字が黒(いわゆるライトモード)というのが一般的ですが、背景の白が眩しくて辛い。筆者は、ダークモード(背景が黒系で文字が白系)が設定できる場合は好んで設定しています。この方が目に優しい。アイキャッチなどの写真も、背景が黒の方が良い感じに見れると思います。

筆者の Blog サイトもダークモード対応を行いました。(2024年3月13日から対応)画面右上の太陽または月アイコンをクリックする事でライトモードとダークモードを切り替えられます。

上のキャプチャ画像は、スマホサイズでのものです。このページを見に来てくれる方は PC の人が多いのですが、スマホ対応もしています。スマホの場合は、PC の時に表示する右側のサイドメニューを表示しません。その代わりに画面右上のハンバーガーメニュー(横3本線のアイコン)をタップする事で、PC のサイドメニューと同様のメニューを表示します。

Tailwind CSS の設定

Tailwind CSS は、CSS フレームワークです。公式のホームページを貼っておきます。公式のドキュメントは見易く、情報も多いのでコーディング時に良く参照しています。

HTMLから離れることなく、モダンなウェブサイトを素早く構築できます。

https://tailwindcss.com/

筆者はちゃんと使い始めてから半年程度です。慣れてくるに従って、便利で手放せないようになりました。今回は、Tailwind を使ってダークモードを実装しました。以下、Next.js と、SvelteKit 両方でダークモードの実装について説明します。

Next.js の場合

Next.js には「next-themes」というライブラリが有るので、これを利用しました。参考にさせていただいたサイトを次に貼るので、具体的にはそちらをご覧ください。

Next.js で next-themes と Tailwind CSS を使って Light/Dark モードを切り替える

https://goodpatch-tech.hatenablog.com/entry/next-themes-tailwind

SvelteKit の場合

SvelteKit の場合は、筆者の実装を紹介しておきます。Tailwind がインストールされていない場合は、次のサイトを参考にインストールします。

Install Tailwind CSS with SvelteKit

https://tailwindcss.com/docs/guides/sveltekit

tailwind.config.js 編集

tailwind.config.js を次のように編集します。

/** @type {import('tailwindcss').Config} */
export default {
  content: ['./src/**/*.{html,js,svelte,ts}'],
  darkMode: 'class',
以下省略

システムまたは web ブラウザの設定に従うだけならば、darkMode: ‘media’ で良く、以下の設定は不要となります。筆者はモード切替を手動で行いたかったので class にしています。

テーマ切り替えボタン実装

テーマ切り替えボタンのコンポーネントを実装して配置します。筆者の実装を例として紹介します。

src/lib/ThemeSwitch.svelte

<script lang="ts">
  let isDark;

  const toggleDarkMode = () => {
    const html = document.documentElement;
    isDark = html.classList.contains('dark')
    if (isDark) {
      html.classList.remove('dark')
      localStorage.setItem('darkMode', 'false')
    } else {
      html.classList.add('dark')
      localStorage.setItem('darkMode', 'true')
    }
  }
</script>

<button
  aria-label="Toggle Dark Mode"
  on:click={() => toggleDarkMode()}
>
  <svg
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 20 20"
    fill="currentColor"
    class="h-6 w-6 text-gray-900 dark:text-gray-100"
  >
    {#if !isDark}
      <path
        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"
      />
    {:else}
      <path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z" />
    {/if}
  </svg>
</button>

上記コードでボタンをクリック・タップした時にモード切り替えを行います。切り替えたモードは、localStorage に保存も行います。ページを開きなおした時に、localStorage を参照してダークモードだった場合は、ダークモードで表示させるため次のコードも必要です。(赤字の部分)

src/app.html

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    (途中省略)
    <script>
      if (typeof window !== 'undefined' && window.localStorage) {
        const darkMode = localStorage.getItem('darkMode');
        if (darkMode === 'true') {
          document.documentElement.classList.add('dark');
        }
      }
      if (typeof window !== 'undefined' && window.matchMedia) {
        const darkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
        if (darkMode) {
          document.documentElement.classList.add('dark');
        }
      }
    </script>
    %sveltekit.head%
  </head>
(以下省略)

SvelteKit のホームページを少しいじって、このボタンを表示するようにしてみます。

src/routes/+page.svelte

<script lang="ts">
  import ThemeSwitch from "$lib/ThemeSwitch.svelte";
</script>

<div class="p-4 dark:bg-zinc-800">
  <ThemeSwitch />
  <p class="text-3xl font-bold dark:text-lime-300">
    ほげhoge
  </p>
</div>

「npm run dev」コマンドでプログラムを実行し、ブラウザで確認してみます。

アイコンをクリックする度にモードが切り替わります。

ダークモードのスタイルを追加

筆者の場合はサイトを公開した後からダークモードを追加しました。そのため、ダークモードのスタイルを追加していく作業が必要で、この作業の方が大変でした。(ダークモード切り替えの仕組みはプログラムなので、理解してしまえば簡単な作業に思えます)

具体的には class=”…. dark:text-lime-300″ のようにダークモード時の色を追加する必要があります。前章のサンプルでは表示文字 (ほげhoge) に、最初から dark:text-lime-300 を入れておきました。この指定が無い場合は黒い文字のままなので、背景色に埋もれてほぼ見えない状態になってしまいます。

終わりに

簡単な紹介のつもりで記事を書き始めました。Next.js については既に分かりやすい記事が多かったので、参考にさせて頂いたサイトの紹介で済ませました。しかし、SvelteKit については記事自体も少なかったので、筆者の実装も含めて具体的に書く結果となりました。少しでも参考になれば幸いです。

【おまけ】本記事のアイキャッチ

ダークモードで黒いものと考えたら「イカ墨パスタ」を連想しました。アイキャッチの写真は、だいぶ前に筆者が撮影したものです。夏の旅行で妙高に宿泊していた時、日本海側へドライブに出掛けて昼食に頂きました。カニ寿司を食べたのですが、あまり見かけない「イカ墨焼きそば」を発見したので頼んでみました。お約束で、口の中とか真っ黒になるのを見たり見せたりして遊んでました。この年の夏はかなり暑くて、食事を終えて車に戻ってみたら、クルマの温度計が上限値を超えたエラー表示をしていました。普段だと、ここまで高温にならないのでこの時初めて見ました。


More Stories

Cover Image for 高品質なスロー静止画アニメーションプログラム(html カスタム要素)を作ってみた

高品質なスロー静止画アニメーションプログラム(html カスタム要素)を作ってみた

NEXT.js
SvelteKit
WordPress
実験
animation
drawImage
requestAnimationFrame

静止画(主に写真を想定)がゆっくりと移動する落ち着いたアニメーションプログラム(html カスタム要素)を作ってみました。静止画(写真)を利用して、手軽にアニメーションできます。高品質な静止画(写真)を高品質なまま、『カクカクしないで滑らかに』アニメーションします。

Cover Image for PageSpeed Insights で携帯パフォーマンススコアを満点 (100) にした

PageSpeed Insights で携帯パフォーマンススコアを満点 (100) にした

実験
AVIF
LCP
PageSpeed Insights
srcset
TBC

実験として PageSpeed Insights の携帯(スマホ)パフォーマンスを満点 (100) になるまでチューニングしたので、その作業内容について記事にしてみました。主に LCP と TBT について改善しています。少しでもパフォーマンスチューニングの参考になれば幸いです。