Tips & Tricks

How to Implement Dark Mode: Claude Code 활용 가이드

implement dark mode: Claude Code 활용. 실용적인 코드 예시와 단계별 가이드를 포함합니다.

ダークモード구현をClaude Code로 효율화하기

ダークモードは今やWeb앱の標準機能ですが、구현にはCSS설계、状態관리、永続化など多くの考慮が필요합니다。Claude Code를 사용하면一貫した설계で短시간で구현할 수 있습니다。

CSS변수에 의한테마설계

まずはCSS변수で테마토큰を定義します。

> CSS변수ベースでライト/ダーク테마を설계して。
> セマンティックな色名を使って、Tailwindと통합できるようにして。
/* globals.css */
:root {
  --color-bg-primary: #ffffff;
  --color-bg-secondary: #f8fafc;
  --color-bg-tertiary: #f1f5f9;
  --color-text-primary: #0f172a;
  --color-text-secondary: #475569;
  --color-text-tertiary: #94a3b8;
  --color-border: #e2e8f0;
  --color-border-hover: #cbd5e1;
  --color-accent: #3b82f6;
  --color-accent-hover: #2563eb;
  --color-surface: #ffffff;
  --color-surface-hover: #f8fafc;
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
}

[data-theme="dark"] {
  --color-bg-primary: #0f172a;
  --color-bg-secondary: #1e293b;
  --color-bg-tertiary: #334155;
  --color-text-primary: #f1f5f9;
  --color-text-secondary: #cbd5e1;
  --color-text-tertiary: #64748b;
  --color-border: #334155;
  --color-border-hover: #475569;
  --color-accent: #60a5fa;
  --color-accent-hover: #93bbfd;
  --color-surface: #1e293b;
  --color-surface-hover: #334155;
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4);
}

테마전환の구현

システム설정連動と手動전환の両方に대응するフックを作ります。

import { useState, useEffect, useCallback } from "react";

type Theme = "light" | "dark" | "system";

export function useTheme() {
  const [theme, setThemeState] = useState<Theme>(() => {
    if (typeof window === "undefined") return "system";
    return (localStorage.getItem("theme") as Theme) || "system";
  });

  const [resolvedTheme, setResolvedTheme] = useState<"light" | "dark">("light");

  const applyTheme = useCallback((newTheme: Theme) => {
    const root = document.documentElement;
    let resolved: "light" | "dark";

    if (newTheme === "system") {
      resolved = window.matchMedia("(prefers-color-scheme: dark)").matches
        ? "dark"
        : "light";
    } else {
      resolved = newTheme;
    }

    root.setAttribute("data-theme", resolved);
    setResolvedTheme(resolved);
  }, []);

  const setTheme = useCallback(
    (newTheme: Theme) => {
      setThemeState(newTheme);
      localStorage.setItem("theme", newTheme);
      applyTheme(newTheme);
    },
    [applyTheme]
  );

  useEffect(() => {
    applyTheme(theme);

    const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
    const handleChange = () => {
      if (theme === "system") applyTheme("system");
    };

    mediaQuery.addEventListener("change", handleChange);
    return () => mediaQuery.removeEventListener("change", handleChange);
  }, [theme, applyTheme]);

  return { theme, setTheme, resolvedTheme };
}

테마전환버튼

import { useTheme } from "@/hooks/useTheme";

export function ThemeToggle() {
  const { theme, setTheme, resolvedTheme } = useTheme();

  const options: { value: Theme; label: string; icon: string }[] = [
    { value: "light", label: "ライト", icon: "sun" },
    { value: "dark", label: "ダーク", icon: "moon" },
    { value: "system", label: "システム", icon: "monitor" },
  ];

  return (
    <div className="flex items-center gap-1 p-1 rounded-lg bg-[var(--color-bg-tertiary)]">
      {options.map((option) => (
        <button
          key={option.value}
          onClick={() => setTheme(option.value)}
          className={`px-3 py-1.5 rounded-md text-sm transition-colors ${
            theme === option.value
              ? "bg-[var(--color-surface)] text-[var(--color-text-primary)] shadow-sm"
              : "text-[var(--color-text-secondary)] hover:text-[var(--color-text-primary)]"
          }`}
          aria-label={option.label}
        >
          {option.label}
        </button>
      ))}
    </div>
  );
}

FOUC(ちらつき)の防止

페이지로딩時に테마が一瞬切り替わるちらつきを防ぐスクリプトです。

<!-- head内に配置 -->
<script>
  (function () {
    const theme = localStorage.getItem("theme") || "system";
    let resolved = theme;
    if (theme === "system") {
      resolved = window.matchMedia("(prefers-color-scheme: dark)").matches
        ? "dark"
        : "light";
    }
    document.documentElement.setAttribute("data-theme", resolved);
  })();
</script>

Tailwind CSSとの통합

// tailwind.config.js
module.exports = {
  darkMode: ["selector", '[data-theme="dark"]'],
  theme: {
    extend: {
      colors: {
        bg: {
          primary: "var(--color-bg-primary)",
          secondary: "var(--color-bg-secondary)",
          tertiary: "var(--color-bg-tertiary)",
        },
        text: {
          primary: "var(--color-text-primary)",
          secondary: "var(--color-text-secondary)",
        },
        surface: {
          DEFAULT: "var(--color-surface)",
          hover: "var(--color-surface-hover)",
        },
      },
    },
  },
};

デザインシステムとの통합はデザインシステム구축を、반응형대응は반응형デザイン를 확인하세요.

정리

Claude Code를 활용하면 CSS변수설계、테마전환ロジック、FOUC防止、Tailwind통합まで、ダークモードの구현を一貫して短시간で완료할 수 있습니다。「ダークモード를 추가해줘」と依頼する만으로、既存프로젝트にも적절하게組み込めます。

자세한 내용은Claude Code공식 문서를 참고하세요.

#Claude Code #dark mode #CSS variables #Tailwind CSS #theming