그럴듯한 개발 블로그
반응형

다크모드 토글버튼을 직접 만들으라네;

공식문서 보고 만들었다.

https://daisyui.com/docs/themes/

 

daisyUI themes — Tailwind CSS Components ( version 4 update is here )

How to use daisyUI themes?

daisyui.com

https://daisyui.com/components/theme-controller/

 

Tailwind Theme Controller Component — Tailwind CSS Components ( version 4 update is here )

Tailwind Theme Controller examples: If a checked checkbox input or a checked radio input with theme-controller class exists in the page, The page will have the same theme as that input's value. component

daisyui.com

로컬스토리지에 theme저장해놓고 쓰게 만들었다. useContext로 provider로 만들어도 되지만 새로고침하면 spa나가면서 깜빡일테니 맘에안들어서 로컬스토리지로 했다.

아 근데 거슬리는게 next에서 SSR시 초깃값을 light로 해놓을 경우 일단 ssr은 light로 하고 hydrate할때 로컬스토리지에 dark있으면 다크모드로 바꿀텐데 이게 좀 거슬린다;

쿠키에 넣어서 하는방법이 있긴 한데 이러면 모든 패킷사이즈가 늘어나니 이거도 거슬린다; 걍 로컬스토리지로 하자 많은 ui라이브러리에서 사용하는 방법이다.

 

1. localStorage에 theme 저장

바꿀때 시간 좀 소요됨 localStorage 사용하므로

2. useContext에 theme 저장

새로고침 등으로 SPA를 나갔다가 오면 테마 유지가 안됨 이건 매우 불편할듯

3. cookie에 theme 저장

SSR시 테마 깜빡이는거 없음 ^오^b 대신 쿠키에 저장하므로 패킷 사이즈 늘어남

 

아래는 토글버튼 localStorage에 테마 저장하는 토글버튼 코드다

SSR안할거면 useState 게으른 초기화 함수에서 window 분기만 빼면 된다.

"use client";
import React, { useState, useEffect } from "react";

export default function ThemeToggleBtn() {
  const [theme, setTheme] = useState<string>(() => {
    if (typeof window !== "undefined") {
      return localStorage.getItem("theme") || "light";
    }
    return "light";
  });

  // 토글 상태 업데이트
  const handleToggle = (e: React.ChangeEvent<HTMLInputElement>) => {
    const newTheme = e.target.checked ? "dark" : "light";
    setTheme(newTheme);
  };

  useEffect(() => {
    localStorage.setItem("theme", theme);
    const localTheme = localStorage.getItem("theme") || "light";
    document.querySelector("html")?.setAttribute("data-theme", localTheme);
  }, [theme]);

  return (
    <button className="btn btn-square btn-ghost">
      <label className="swap swap-rotate">
        <input
          type="checkbox"
          onChange={handleToggle}
          checked={theme !== "light"}
        />
        <svg
          className="swap-on fill-current w-10 h-10"
          xmlns="http://www.w3.org/2000/svg"
          viewBox="0 0 24 24"
        >
          <path d="M5.64,17l-.71.71a1,1,0,0,0,0,1.41,1,1,0,0,0,1.41,0l.71-.71A1,1,0,0,0,5.64,17ZM5,12a1,1,0,0,0-1-1H3a1,1,0,0,0,0,2H4A1,1,0,0,0,5,12Zm7-7a1,1,0,0,0,1-1V3a1,1,0,0,0-2,0V4A1,1,0,0,0,12,5ZM5.64,7.05a1,1,0,0,0,.7.29,1,1,0,0,0,.71-.29,1,1,0,0,0,0-1.41l-.71-.71A1,1,0,0,0,4.93,6.34Zm12,.29a1,1,0,0,0,.7-.29l.71-.71a1,1,0,1,0-1.41-1.41L17,5.64a1,1,0,0,0,0,1.41A1,1,0,0,0,17.66,7.34ZM21,11H20a1,1,0,0,0,0,2h1a1,1,0,0,0,0-2Zm-9,8a1,1,0,0,0-1,1v1a1,1,0,0,0,2,0V20A1,1,0,0,0,12,19ZM18.36,17A1,1,0,0,0,17,18.36l.71.71a1,1,0,0,0,1.41,0,1,1,0,0,0,0-1.41ZM12,6.5A5.5,5.5,0,1,0,17.5,12,5.51,5.51,0,0,0,12,6.5Zm0,9A3.5,3.5,0,1,1,15.5,12,3.5,3.5,0,0,1,12,15.5Z" />
        </svg>
        <svg
          className="swap-off fill-current w-10 h-10"
          xmlns="http://www.w3.org/2000/svg"
          viewBox="0 0 24 24"
        >
          <path d="M21.64,13a1,1,0,0,0-1.05-.14,8.05,8.05,0,0,1-3.37.73A8.15,8.15,0,0,1,9.08,5.49a8.59,8.59,0,0,1,.25-2A1,1,0,0,0,8,2.36,10.14,10.14,0,1,0,22,14.05,1,1,0,0,0,21.64,13Zm-9.5,6.69A8.14,8.14,0,0,1,7.08,5.22v.27A10.15,10.15,0,0,0,17.22,15.63a9.79,9.79,0,0,0,2.1-.22A8.11,8.11,0,0,1,12.14,19.73Z" />
        </svg>
      </label>
    </button>
  );
}
반응형
profile

그럴듯한 개발 블로그

@donghyk2

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!