2025.05.19

Gatsby × Tailwindcss × ダークモードでSEO対策

Shuji
- Gatsby
- UI
- SEO
ダークモードのSEO効果
ダークモード自体には直接的なSEO効果はないものの、眼精疲労や睡眠障害などへの配慮を行うことによるユーザー体験の上昇は、離脱率を下げ、間接的にSEOに好影響あると言えます。この記事ではGatsby.jsとTailwindcssでダークモードの実装を行いました。
コンテキストでテーマ管理
ThemeContextを作成してdark
, light
, system
の3つを、グローバルに状態管理を行います。
import React, { createContext, useContext, useEffect, useState } from "react"
type Theme = "dark" | "light" | "system"
interface ThemeContextType {
theme: Theme
setTheme: (theme: Theme) => void
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined)
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<Theme>("system")
useEffect(() => {
const savedTheme = localStorage.getItem("theme") as Theme
if (savedTheme) {
setTheme(savedTheme)
}
}, [])
useEffect(() => {
const root = window.document.documentElement
root.classList.remove("light", "dark")
if (theme === "system") {
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
: "light"
root.classList.add(systemTheme)
} else {
root.classList.add(theme)
}
localStorage.setItem("theme", theme)
}, [theme])
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
)
}
export function useTheme() {
const context = useContext(ThemeContext)
if (context === undefined) {
throw new Error("useTheme must be used within a ThemeProvider")
}
return context
}
トグルボタンの用意
import React from "react"
import { useTheme } from "@/contexts/ThemeContext"
import { Sun, Moon, Monitor } from "lucide-react"
export function ThemeToggle() {
const { theme, setTheme } = useTheme()
return (
<div className="flex items-center space-x-2">
<button
onClick={() => setTheme("light")}
className={`p-2 rounded-lg ${
theme === "light"
? "bg-primary text-primary-foreground"
: "bg-muted text-muted-foreground"
}`}
>
<Sun className="w-5 h-5" />
</button>
<button
onClick={() => setTheme("dark")}
className={`p-2 rounded-lg ${
theme === "dark"
? "bg-primary text-primary-foreground"
: "bg-muted text-muted-foreground"
}`}
>
<Moon className="w-5 h-5" />
</button>
<button
onClick={() => setTheme("system")}
className={`p-2 rounded-lg ${
theme === "system"
? "bg-primary text-primary-foreground"
: "bg-muted text-muted-foreground"
}`}
>
<Monitor className="w-5 h-5" />
</button>
</div>
)
}
スタイルの切り替え
スタイルの切り替えは、上記のようにuseThemeを利用するか、Tailwindのdark:
修飾子を使用、もしくは次のようにcssで定義します。
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
/* 中略 */
}
グローバルなラッパーコンポーネントの設定
gatsby-browser.jsで、GatsbyのwrapRootElement関数と先ほどのThemeProviderで、アプリケーション全体をラップするコンポーネントを定義します。
import React from "react"
import { ThemeProvider } from "./src/contexts/ThemeContext"
import "./src/styles/global.css"
export const wrapRootElement = ({ element }) => {
return <ThemeProvider>{element}</ThemeProvider>
}
SSR対応
gatsby-browser.jsではクライアントサイドでのハイドレーション時にテーマを適用しました。同様に サーバーサイドでのレンダリング時にはgatsby-ssr.jsでテーマを適用し、これによりページの初期表示時からテーマが正しく適用されます。
import React from "react"
import { ThemeProvider } from "./src/contexts/ThemeContext"
import "./src/styles/global.css"
export const wrapRootElement = ({ element }) => {
return <ThemeProvider>{element}</ThemeProvider>
}
/**
* @type {import('gatsby').GatsbySSR['onRenderBody']}
*/
export const onRenderBody = ({ setHtmlAttributes }) => {
setHtmlAttributes({ lang: `en` })
}
最後にトグルボタンを設置したい場所にThemeToggleを置いて完了です。
import { ThemeToggle } from "@/components/ui/ThemeToggle"
const Footer = () => (
<footer className="bg-white dark:bg-black">
<ThemeToggle />
</footer>
)
export default Footer
最後に
ダークモード対応は、実装コストはかかるものの、間接的なSEO効果や、ユーザー体験の向上が期待できるため、Webサイトの目的やユーザーのニーズに応じて、ぜひ導入を検討してみてはいかがでしょうか!
その他の記事
Other Articles
FastAPI × React LeafletでGoogle Earth Engineの衛星データを扱う
Google Earth Engine で衛生データを表示する
DifyのワークフローをNode Cronで定期実行
Difyの外部ナレッジAPIでNotion APIのデータを取得
Dify × NotionでAIチャットボットを構築する
[Gatsby.js] gatsby-plugin-canonical-urlsでcanonicalタグを生成する
[Gatsby.js] gatsby-plugin-robots-txtでrobot.txtを生成する
[Gatsby.js] gatsby-plugin-sitemapでサイトマップを生成する
Difyのワークフローでスクレイピングしたデータを外部アプリへPOST
Difyの外部ナレッジAPIでRAGを拡張する
DifyとRemixの連携によるチャットボット構築
Remix × Vitest で Login API をテストする
GatsbyでGifを動作させる(TypeScript)
RemixでStripeを使った決済機能の導入