카테고리 없음

vite에서 emotion 사용하기, 다크모드 환경 설정하기

이즈흐 2024. 1. 19. 18:51

 

1. emotion 설치 및 emotion/styled 설치

npm install --save @emotion/react
npm install --save @emotion/styled

 

2. tsconfig.json에 아래 코드 추가

{
  "compilerOptions": {
    
    ...

    "jsxImportSource": "@emotion/react"
  },

}

3. vite에는 따로 babel 플러그인을 설치하거나 설정안해도 됨

 

 

GlobalStyle과 다크모드를 위한 Theme 설정하는 법

이를 위해서 3개의 파일이 필요하다.

GlobalStyle.ts

Theme.ts

useTheme.ts

1. Theme.ts

- 먼저 Theme의 타입을 지정해줘야한다.

- 해당 타입은 다크모드가 적용되는 styled 컴포넌트에 theme 데이터를 사용할 때 지정해줘야한다.

- light와 dark라는 객체를 정의하고 이를 mode객체에 감싸서 내보낸다.

export interface Theme {
  bgColor: string
  fontColor: string
}
interface ThemeGroup {
  light: Theme
  dark: Theme
}
/**
 * @light theme
 */

export const light: Theme = {
  bgColor: '#fff',
  fontColor: '#000'
}

/**
 * @dark theme
 */

export const dark: Theme = {
  bgColor: '#000',
  fontColor: '#eee'
}

const mode: ThemeGroup = {
  light,
  dark
}

export default mode

 

2.GlobalStyle.ts

- Theme.ts에서 정의된 테마 값으로 global스타일을 지정할 것이다.

- 매개변수로 theme값을 가져오고. body에 해당 theme값의 bgcolor와 fontcolor를 지정한다.

import { css } from '@emotion/react'
import { Theme } from './Theme'

const GlobalStyle = (theme: Theme) => css`
  * {
    margin: 0;
    padding: 0;
    -moz-osx-font-smoothing: grayscale;
    -webkit-font-smoothing: antialiased;
    box-sizing: border-box;
  }
  body {
    background-color: ${theme.bgColor};
    color: ${theme.fontColor};
  }
`

export default GlobalStyle

 

3. useTheme.ts

- getInitialTheme로 로컬스토리지에 현재 다크모드 값을 저장하고 다시 돌아왔을 때 다크모드인지 라이트 모드인지를 확인하고, 만약 theme값이 없거나 이상한 값이 들어있다면 사용자의 운영체제에서 선호하는 색상 테마를 가져와서 그 테마로 설정한다.

- 그리고 해당 값을 useState에 저장하고, toggleTheme함수로 바꿀 수 있도록 한다.

- 이때 로컬 스토리지의 값이 계속 바뀌어야한다.

import { useCallback, useEffect, useState } from 'react'

const useTheme = (): [typeof theme, typeof toggleTheme] => {
  const getInitialTheme = useCallback(() => {
    let theme = window.localStorage.getItem('app_theme') as
      | 'light'
      | 'dark'
      | null
    const INVALID_THEME = theme !== 'light' && theme !== 'dark'

    if (!theme || INVALID_THEME) {
      const { matches } = window.matchMedia('(prefers-color-scheme: dark)')
      theme = matches ? 'dark' : 'light'
    }

    return theme
  }, [])

  const [theme, setTheme] = useState(getInitialTheme)

  const toggleTheme = useCallback(() => {
    setTheme(prevTheme => (prevTheme === 'dark' ? 'light' : 'dark'))
  }, [])

  useEffect(() => {
    window.localStorage.setItem('app_theme', theme)
  }, [theme])

  return [theme, toggleTheme]
}

export default useTheme

 

 

결과적으로 App.tsx에 설정하는 법

import { Global, ThemeProvider } from '@emotion/react'
import styled from '@emotion/styled'
import useTheme from './styles/useTheme'
import GlobalStyle from './styles/GlobalStyles'
import { default as THEME } from './styles/Theme'
import ThemeSwitch from './components/ThemeSwitch'

function App() {
  const [theme, onToggle] = useTheme()

  return (
    <>
      <ThemeProvider theme={THEME[theme]}>
        <Global styles={GlobalStyle(THEME[theme])} />
        <MainContainer>
          <ThemeSwitch
            checked={theme === 'dark'}
            toggleSwitch={onToggle}
          />
          <ProfileContainer>dsadsdadsasda</ProfileContainer>
        </MainContainer>
      </ThemeProvider>
    </>
  )
}

export default App

const MainContainer = styled.div`
  margin: 0 auto;
  max-width: 780px;
`
const ProfileContainer = styled.div`
  margin: 1rem;
`

- emotion의 Theming을 사용 - ThemeProvider를 이용해서 theme라는 값을 하위에서도 사용가능하도록 한다.

 -> 스타일이 props.theme지정된 구성 요소로 테마에 액세스하거나 테마를 CSS 소품으로 허용하는 기능을 제공하세요.

 

- emotion의 Global을 사용 - 전역 CSS를 적용하기 위해 사용  Global 태그 아래로 다른 컴포넌트를 넣어주면 Global style이 적용된다.

 

- ThemeSwitch로 간단하게 버튼을 클릭하면 theme값이 바뀌도록 함

 

 

 

 

결과

 

 

참고 자료

https://emotion.sh/docs/install

https://emotion.sh/docs/theming

https://emotion.sh/docs/typescript

https://emotion.sh/docs/globals

https://velog.io/@pius712/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%8B%A4%ED%81%AC%EB%AA%A8%EB%93%9C-emotion

https://velog.io/@godud2604/Emotion-%EC%9C%BC%EB%A1%9C-Dark-Mode-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0

728x90
반응형
LIST