다크모드 토글버튼을 직접 만들으라네;
공식문서 보고 만들었다.
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>
);
}