[TIL] Dark Mode

심지훈·2021년 8월 21일
0

TIL

목록 보기
2/5

이번에는 다크모드를 배웠습니다.
바닐라 스크립트는 아래를 참고 했고
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 마크업은 위와 같다. inputlabel을 엮어주고
네이버 부스트코스에서 배운대로 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에 저장된 색상값을 MainBackground-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,
};
profile
유연한 개발자

0개의 댓글