반응형
React에서 간단하게 다크 모드를 구현해보려고 한다.
폴더 구조
├─assets
│ └─svgs
├─routes
│ ├─Home
│ ├─Weather
│ └─_shared
│ └─GNB
├─states
└─types
테마 구분
states/theme.ts
import { atom } from 'recoil'
const initTheme = localStorage.getItem('theme') || 'light'
export const themeState = atom({
key: '#themeState',
default: initTheme,
})
간단하게 recoil의 atom으로 전역 상태를 만들어주었다. 기본값으로는 로컬 스토리지에 저장해 놓은 테마를, 없을 경우 밝은 모드를 지정해주었다.
routes/index.tsx
html의 data 속성을 통해 테마를 구분하도록 할 것이다. 밝은 테마이면 'light'를, 어두운 테마이면 'dark'를 지정해주었다.
import { useEffect } from 'react'
import { Routes, Route } from 'react-router-dom'
import { useRecoilValue } from 'recoil'
import { themeState } from 'states/theme'
import styles from './routes.module.scss'
import HomePage from './Home'
import Weather from './Weather'
import GNB from './_shared/GNB'
const App = () => {
const theme = useRecoilValue(themeState)
useEffect(() => {
document.documentElement.setAttribute('color-theme', theme)
}, [theme])
return (
<div className={styles.container}>
<GNB />
<main className={styles.app}>
<Routes>
<Route path='/' element={<HomePage />} />
<Route path='weather' element={<Weather />} />
</Routes>
</main>
</div>
)
}
export default App
먼저 처음 컴포넌트가 마운트될 때 'color-theme'에 테마값을 지정해주도록 하였다.
위에서 만든 themeState에서 로컬 스토리지에 값이 있다면 해당 값이 color-theme에 저장될 것이고, 값이 없다면 'light'가 저장될 것이다.
개발자 도구를 열어 element를 확인해보면 아래와 같이 color-theme이 잘 지정된 것을 확인할 수 있다.
routes/_shared/GNB.tsx
기본 테마 지정이 완료되었다면 이제 테마 변경 기능을 구현할 차례이다.
import { useRecoilState } from 'recoil'
import { themeState } from 'states/theme'
import styles from './gnb.module.scss'
import { Moon, Sun } from 'assets/svgs'
const GNB = () => {
// ...
const [theme, setTheme] = useRecoilState(themeState)
const handleThemeClick = () => {
const newTheme = theme === 'dark' ? 'light' : 'dark'
localStorage.setItem('theme', newTheme)
setTheme(newTheme)
}
return (
<nav className={styles.gnb}>
<ul>
// ...
<li>
<button type='button' title={theme} onClick={handleThemeClick} className={styles.themeBtn}>
{theme === 'light' ? <Sun data-icon='light' /> : <Moon data-icon='dark' />}
</button>
</li>
</ul>
</nav>
)
}
export default GNB
전역 상태 themeState를 가져와준 후, 테마 변경 버튼을 누를 때마다 로컬 스토리지에 값을 새로 저장해주고, themeState도 변경해주면 된다.
css 적용
이제 테마 상태에 맞게 css로 색상을 변경해주면 된다. html의 data 속성을 사용했기 때문에 굳이 각각 새로 클래스명을 지정해 줄 필요가 없다.
routes/Home/home.module.scss
@use 'src/styles/constants/colors';
.container {
height: 100vh;
padding: 20px;
transition: 300ms background-color;
:root[color-theme='dark'] & {
color: colors.$WHITE;
background-color: colors.$GRAY2;
}
}
각각의 scss 파일에서 :root[color-theme='dark']에서 색상을 변경해주면 된다.
routes/_shared/GNB/gnb.module.scss
@use 'src/styles/mixins/flexbox';
@use 'src/styles/constants/colors';
.gnb {
border-bottom: 1px solid colors.$GRAYE;
ul {
@include flexbox.flexbox(center, center);
padding: 20px 0;
transition: 300ms background-color;
// ...
}
}
:root[color-theme='dark'] & {
border-bottom: 1px solid colors.$GRAY3;
ul {
background-color: colors.$GRAY2;
// ...
}
}
}
반응형
'FE > React' 카테고리의 다른 글
[React] Storybook 사용해보기 (0) | 2022.12.02 |
---|---|
React Infinite Carousel 만들기(TS) (1) | 2022.11.24 |
React 스톱워치 구현하기(TS) (0) | 2022.11.17 |
React 프로젝트에 i18n으로 다국어 지원하기 (0) | 2022.10.31 |
React의 LifeCycle (0) | 2021.06.30 |