Vanilla Js로 다크모드를 구현 해보겠습니다. 우선 클래스를 사용 해본적이 별로 없기 때문에 클래스를 사용해서 구현해봤습니다.
prefers-color-scheme
를 사용해서 브라우저 설정에 따라 자동으로 다크모드를 감지하므로 사용자의 환경을 잘 반영할 수 있는 장점은 있지만, 모든 브라우저에서 지원되는 것은 아니며, 구형 브라우저에서는 동작하지 않을 수 있는 단점이 존재하기 때문에 이것으로 구현하지 않았습니다.
setAttribute 메서드를 사용
하여 최상단 컴포넌트(App 컴포넌트)에 dark 또는 light 속성 값을 추가하는 방식으로 다크모드를 구현했습니다. 사용자의 환경을 자동으로 감지하지 않는 단점이 있지만, 사용자가 직접 버튼을 클릭하여 다크모드를 변경하는 방식이 더 좋다고 판단하여 prefers-color-scheme 방식보다는 setAttribute를 사용했습니다. 이러한 방식으로 다크모드를 구현하면 사용자가 직접 제어할 수 있으며, 브라우저 설정에 의존하지 않기 때문에 호환성 문제도 최소화됩니다.
DarkModeToggle 클래스 인스턴스를 만들어줍니다.
class App {
$target = null;
constructor($target) {
this.$target = $target;
this.darkModeToggle = new DarkModeToggle({
$target,
});
}
}
DarkModeToggle.js 파일을 script로 추가해줍니다.
참고로 main.js파일은 App클래스를 생성해주는 파일입니다.<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel="stylesheet" href="src/style.css" />
<title>Cat Search</title>
</head>
<body>
<div id="App"></div>
<script src="src/DarkModeToggle.js"></script>
<script src="src/App.js"></script>
<script src="src/main.js"></script>
</body>
</html>
DarkModeToggle 클래스를 작성 해줍니다.
createElement를 사용해 input을 만들어줍니다.
클래스 내부에서 사용할 수 있도록 this.$darkModeBtn에 연결해줍니다.
input의 type을 checkbox로 추가 해줍니다.
$target을 통해 DOM에 appendChild를 통해 추가해줍니다.
change Event를 통해 check될때, check되지 않을때 color-mode 속성으로 'dark', 'light'를 지정해서 다크모드를 구현합니다.
하단에 color-mode가 바뀌는걸 볼 수 있습니다.
class DarkModeToggle {
isDarkMode = null;
constructor({ $target }) {
const $darkModeBtn = document.createElement('input');
this.$darkModeBtn = $darkModeBtn;
this.$darkModeBtn.type = 'checkbox';
this.$darkModeBtn.className = 'DarkModeBtn';
$target.appendChild($darkModeBtn);
this.$darkModeBtn.addEventListener('change', (e) => {
this.setColorMode(e.target.checked);
});
// 업데이트
this.initialColorMode();
}
initialColorMode() {
this.$darkModeBtn.checked = this.isDarkMode;
this.setColorMode(this.isDarkMode);
}
setColorMode(isDarkMode) {
document.documentElement.setAttribute('color-mode', isDarkMode ? 'dark' : 'light');
}
render() {}
}
color-mode가 dark인 경우, light인 경우에 따라 css를 변경
해주면 됩니다.:root[color-mode='dark'] {
--background: #000;
--textColor: white;
}
:root[color-mode='light'] {
--background: white;
--textColor: #000;
}
body {
background-color: var(--background);
color: var(--textColor);
}
// App.tsx
import { useState } from 'react';
import styled from 'styled-components';
const DarkModeBtn = styled.button`
원하는 스타일대로 작성
`;
function App() {
const [BlackAndWhiteBtn, setBlackAndWhiteBtn] = useState(true);
const togleBtn = () => {
setBlackAndWhiteBtn((prev) => !prev);
};
return (
<>
<DarkModeBtn onClick={togleBtn}>다크모드</DarkModeBtn>
</>
);
}
export default App;
// styled.d.ts
import 'styled-components';
declare module 'styled-components' {
export interface DefaultTheme {
bgColor: string;
textColor: string;
boxShadow: string;
}
export interface DarkTheme {
bgColor: string;
textColor: string;
boxShadow: string;
}
}
// theme.ts
import { DefaultTheme, DarkTheme } from 'styled-components';
export const Default: DefaultTheme = {
bgColor: 'rgba(236, 240, 241,1.0)',
textColor: '#2c3e50',
boxShadow: '0px 0px 5px rgb(0, 0, 0, 0.2)',
};
export const Dark: DarkTheme = {
bgColor: '#2c3e50',
textColor: 'rgba(236, 240, 241,1.0)',
boxShadow: '0px 0px 5px rgb(255, 255, 255, 0.2)',
};
// App.tsx
import { useState } from 'react';
import GlobalStyle from './GlobalStyle';
import styled, { ThemeProvider } from 'styled-components';
import { Dark, Default } from './theme';
const DarkModeBtn = styled.button`
border: none;
padding: 10px;
position: fixed;
bottom: 10px;
right: 10px;
background-color: inherit;
border: 1px solid ${(props) => props.theme.textColor};
border-radius: 20px;
cursor: pointer;
color: ${(props) => props.theme.textColor};
`;
function App() {
const [isDarkMode, setIsDarkMode] = useState(false);
const togleBtn = () => {
setIsDarkMode((prev) => !prev);
};
return (
<>
<ThemeProvider theme={isDarkMode ? Dark : Default}>
<GlobalStyle />
<DarkModeBtn onClick={togleBtn}>다크모드</DarkModeBtn>
</ThemeProvider>
</>
);
}
export default App;
const DarkModeBtn = styled.button`
color: ${(props) => props.theme.textColor};
`;
body{
background-color: ${(props) => props.theme.bgColor};
};
Vanilla JS 클래스형으로 구현한 경우 JavaScript 코드를 직접 작성해야 하고 다크모드를 적용하는 컴포넌트가 늘어나면 관리가 어려울 것 같다는 생각이 듭니다.
반면에 React의 styled-components를 사용하여 다크모드를 적용하는 컴포넌트가 많아져도 관리 하기 수월할 것 같다는 생각이 드네요.
Vanilla Js와 React 2가지 다크모드를 구현하면서 라이브러리의 사용성과 생산성을 다시 한 번 느낄 수 있었습니다. 라이브러리를 활용하면 작업량이 줄어들고 코드의 일관성과 유지보수성을 높일 수 있다는걸 많이 느꼈습니다. 라이브러리 좋아요! 하지만 지금은 배우는 단계이기 때문에 라이브러리 사용 보다는 순수 Vanilla Js로 구현해보며 기본 원리를 배우는데 집중 해야겠습니다.