React 다크 모드 구현하기(with Recoil, SCSS)

2022. 11. 6. 23:16·FE/React
반응형

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
'FE/React' 카테고리의 다른 글
  • React Infinite Carousel 만들기(TS)
  • React 스톱워치 구현하기(TS)
  • React 프로젝트에 i18n으로 다국어 지원하기
  • React의 LifeCycle
SH_Roh
SH_Roh
  • SH_Roh
    혼자공부끄적끄적
    SH_Roh
  • 전체
    오늘
    어제
    • 분류 전체보기 (159)
      • FE (39)
        • HTML, CSS (3)
        • Javascript (17)
        • React (11)
        • Next.js (4)
      • Network (1)
      • DevOps (4)
      • Git (1)
      • Trouble Shooting (24)
      • Algorithm (41)
        • Python (2)
        • Data Structure, Algorithm (7)
        • Problem Solving (31)
      • Education (23)
        • Elice AI Track (4)
        • Wanted Pre-Onboarding FE Co.. (19)
      • TIL (25)
      • Etc. (1)
        • 회고 (1)
        • 그냥저냥 (0)
  • 링크

    • Github
  • 인기 글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
SH_Roh
React 다크 모드 구현하기(with Recoil, SCSS)
상단으로

티스토리툴바