이번에는 다크모드를 배웠습니다.
바닐라 스크립트는 아래를 참고 했고
https://velog.io/@yijaee/다크모드-구현하기
리액트는 아래를 참고하였습니다.
https://velog.io/@gparkkii/reactdarkmode
감사합니다.
<div class="container">
<p class="txt">Hello World</p>
<input type="checkbox" id="darkModeCheck" name="chk">
<label for="darkModeCheck">
<div class="emoji">
<span class="moon">🌝</span>
<span class="sun">🌞</span>
</div>
</label>
</div>
HTML 마크업은 위와 같다. input
과 label
을 엮어주고
네이버 부스트코스에서 배운대로 CSS
를 이용해 토글버튼을 만들었습니다.
input[type="checkbox"] {
/* input 감추기 */
position: absolute;
overflow: hidden;
width: 1px;
height: 1px;
margin: -1px;
opacity: 0;
}
input[type="checkbox"] + label {
display: inline-block;
overflow: hidden;
position: relative;
width: 42px;
height: 21px;
border-radius: 10px;
background: #666;
vertical-align: top;
color: transparent;
border: 1px solid #fff;
cursor: pointer;
}
input[type="checkbox"] + label:after {
content: "";
position: absolute;
top: 1px;
left: 1px;
width: 19px;
height: 19px;
background: #fff;
border-radius: 50%;
}
input[type="checkbox"]:checked + label {
background-color: #62a7ee;
}
input[type="checkbox"]:checked + label:after {
left: auto;
right: 1px;
}
바닐라 스크립트로 구현하는 것은 은근 간단했습니다. 토글버튼이 클릭될때마다 컨테이너 background-color
css
속성을 바꿔주면 됬습니다. 또 그리고 블로그에서 작성하신대로 그냥 구현하면 화면을 리프레시하거나 재접속시에 기존에 저장했던 상태를 잃어버리기 때문에
localStorage
에 정보를 저장합니다.
클릭때마다 HTML 제일 상단 노드 documentElement에 color-mode
라는 사용자 속성을 추가해 컬러 스키마를 변경합니다.
전체 코드는 아래와 같습니다.
const $check = document.getElementById("darkModeCheck");
const colorMode = "color-mode";
const light = "light";
const dark = "dark";
const isUserColorTheme = localStorage.getItem(colorMode);
const isOsColorTheme = window.matchMedia("(prefers-color-scheme: dark").matches
? dark
: light;
const getUserColorMode = () =>
isUserColorTheme ? isUserColorTheme : isOsColorTheme;
window.addEventListener("load", () => {
if (getUserColorMode() === dark) {
localStorage.setItem(colorMode, dark);
document.documentElement.setAttribute(colorMode, dark);
$check.setAttribute("checked", true);
} else {
localStorage.setItem(colorMode, light);
document.documentElement.setAttribute(colorMode, light);
}
});
$check.addEventListener("click", (e) => {
if (e.target.checked) {
document.documentElement.setAttribute(colorMode, dark);
localStorage.setItem(colorMode, dark);
} else {
document.documentElement.setAttribute(colorMode, light);
localStorage.setItem(colorMode, light);
}
});
바닐라 스크립트보다는 아무래도 이것저것 할께 많습니다.
리액트 버전에서는 Context API
을 통해 전역적으로 색상을 변경합니다. (만약 다른 전역 상태관리 방법을 쓴다면 어떤것이든 상관없습니다.)
우선 최상단 구조를 보자면 아래와 같습니다.
function App() {
return (
<ThemeComponent>
<Main />
</ThemeComponent>
);
}
<Main/>
은 컨텐츠가 있는 부분이고 <ThemeComponent>
는
Context API
를 사용한 컴포넌트입니다. 이렇게 되면 ThemeCompnent (Context API)
를 전역적으로 사용할 수 있어
Main
컨텐츠 속에서 색상 모드를 변경 할 수 있는 버튼을 사용 할 수 있게 됩니다.
<ThemeComponent>
는 아래와 같습니다.
import React, { createContext, useState, useContext, useCallback } from "react";
import { lightMode, darkMode } from "../theme/colorScheme";
import { ThemeProvider } from "styled-components";
const ThemeContext = createContext();
function ThemeComponent({ children }) {
const localTheme = localStorage.getItem("color-theme") || "light";
const [themeMode, setThemeMode] = useState(localTheme);
const themeObj = themeMode === "light" ? lightMode : darkMode;
return (
<ThemeContext.Provider value={{ themeMode, setThemeMode }}>
<ThemeProvider theme={themeObj}>{children}</ThemeProvider>
</ThemeContext.Provider>
);
}
function useTheme() {
const context = useContext(ThemeContext);
const { themeMode, setThemeMode } = context;
const toggleTheme = useCallback(() => {
if (themeMode === "light") {
setThemeMode("dark");
localStorage.setItem("color-theme", "dark");
} else {
setThemeMode("light");
localStorage.setItem("color-theme", "light");
}
}, [themeMode]);
return [themeMode, toggleTheme];
}
export { ThemeComponent, useTheme };
전역적으로 2개의 상태를 정의하였지만 사용자 정의 훅 useTheme
을 통해 사용자는 색상변경에 필요한 toggle 버튼
과 themeMode 현재모드
를 전역적으로 가져 올 수 있습니다.
스타일드 컴포넌트에서 제공하는 ThemeProvider
를 통해 현재 라이트 모드인지 ? 다크 모드인지를 알 수 있는 객체(themeMode
)를 전달받으면서 각 모드에 따라 themeMode
에 저장된 색상값을 Main
의 Background-color
로 사용한다.
export const lightMode = {
bgColor: "#F8F7F4",
textColor: "#31302E",
borderColor: "1px solid #eaeaea",
};
export const darkMode = {
bgColor: "#1E1E22",
textColor: "#ccc",
borderColor: "1px solid #2c2d33",
};
export const theme = {
darkMode,
lightMode,
};