CSS 변수로 다크·화이트 모드 전환 만들기 (깜빡임 없이)
다크·화이트 모드 전환은 색상을 CSS 변수로 모아 두고, html 태그의 속성 하나로 변수 값을 바꾸면 돼요. 이 사이트 오른쪽 위의 전환 버튼이 이 방식이에요.
1. 색상을 변수로 모으기
색을 요소마다 직접 쓰지 않고 변수로만 가리켜요. 모드가 바뀌면 변수 값만 달라져요.
:root {
--bg: #0d1117;
--text: #e6edf3;
--accent: #f2b84b;
}
html[data-theme="light"] {
--bg: #f6f8fa;
--text: #1f2630;
--accent: #9a5b00; /* 흰 배경에서도 읽히도록 더 어둡게 */
}
body { background: var(--bg); color: var(--text); }
a { color: var(--accent); }
2. 화면이 그려지기 전에 모드 적용하기
이 부분이 핵심이에요. 스크립트를 페이지 맨 아래에 두면, 밝은 모드를 고른 사용자가 새로고침할 때 어두운 화면이 잠깐 보였다가 바뀌어요. head 맨 위에 짧은 스크립트를 넣어서 화면이 그려지기 전에 속성을 정해요.
// head 맨 위의 script 태그 안에 넣는다
(function () {
var t = "dark";
try {
var s = localStorage.getItem("theme");
if (s === "light" || s === "dark") { t = s; }
} catch (e) {}
document.documentElement.setAttribute("data-theme", t);
})();
3. 버튼으로 바꾸고 기억하기
document.addEventListener("click", function (e) {
var btn = e.target.closest(".theme-toggle");
if (!btn) { return; }
var root = document.documentElement;
var next = root.getAttribute("data-theme") === "light" ? "dark" : "light";
root.setAttribute("data-theme", next);
try { localStorage.setItem("theme", next); } catch (err) {}
btn.setAttribute("aria-label", next === "light" ? "다크 모드로 전환" : "화이트 모드로 전환");
});
실수하기 쉬운 부분
- 포인트 색을 그대로 쓰면 안 돼요. 어두운 배경에서 잘 보이던 노란색은 흰 배경에서 글자로 쓰면 거의 안 읽혀요. 밝은 모드용으로 더 어두운 색을 따로 정해야 해요.
- 색을 직접 지정한 요소가 남아 있으면 깨져요. 본문 어딘가에
color: #fff가 박혀 있으면 밝은 모드에서 사라져요. 전부 변수로 바꿔야 해요. - 버튼은 진짜 button으로 만드세요.
div에 클릭만 달면 키보드로 누를 수 없어요.aria-label도 현재 상태에 맞게 바꿔 줘야 해요. - localStorage는 실패할 수 있어요. 사생활 보호 모드에서는 오류가 나기도 해서
try로 감싸요. - 서버 캐시와 충돌하지 않아요. 모드를 서버가 아니라 방문자의 브라우저에서 정하기 때문에, 페이지를 캐시해도 문제가 없어요.
확인 범위
이 방식은 이 사이트에 적용해 전환, 새로고침 후 유지, 두 모드의 글자 대비를 확인했어요. 운영체제의 다크 모드 설정을 따라가게 하려면 prefers-color-scheme 확인을 2번 스크립트에 더하면 돼요. 그 부분은 여기서 시험하지 않았어요.