styled-components
설치
npm i styled-components
style/theme.ts
light 모드와 dark 모드의 css를 다음과 같이 지정한다.
export type ThemeName = 'light' | 'dark';
type ColorKey = 'primary' | 'background' | 'secondary' | 'third';
interface Theme {
name: ThemeName,
color: Record<ColorKey, string>;
}
export const light: Theme = {
name: 'light',
color: {
primary: 'brown',
background: 'lightgray',
secondary: 'blue',
third: 'green'
}
};
export const dark: Theme = {
name: 'dark',
color: {
primary: 'coral',
background: 'midnightblue',
secondary: 'darkblue',
third: 'darkgreen'
}
}
export const getTheme = (themeName: ThemeName): Theme => {
switch (themeName) {
case 'light':
return light;
case 'dark':
return dark;
}
}
style/global.ts
전역 스타일을 지정하는 파일이다.
props로 테마 이름을 받는다. GlobalStyle을 export 했기 때문에 외부에서 컴포넌트로 사용할 수 있다.
import "sanitize.css";
import { createGlobalStyle } from "styled-components";
import { ThemeName } from "./theme";
interface Props {
themeName: ThemeName;
}
export const GlobalStyle = createGlobalStyle<Props>`
body {
margin: 0;
padding: 0;
background-color: ${(props) => props.themeName === 'light' ? 'white' : 'black'};
h1 {
margin: 0;
}
* {
color: ${(props) => props.themeName === 'light' ? 'black' : 'white'}
}
`;
테마 스위치
App.tsx
BookStoreThemeProvider로 다른 컴포넌트들을 감싸서 테마가 적용될 수 있도록 한다.
import Layout from "./components/layout/Layout"
import Home from "./pages/Home"
import ThemeSwitcher from "./components/header/ThemeSwitcher"
import { BookStoreThemeProvider } from "./context/themeContext"
function App() {
return (
<>
<BookStoreThemeProvider>
<ThemeSwitcher />
<Layout>
<Home />
</Layout>
</BookStoreThemeProvider>
</>
)
}
context/themeContext
ThemeContext의 하위 컴포넌트에서도 현재 테마 이름과 테마를 토글 할 수 있는 함수에 접근할 수 있도록 ThemeContext를 선언한다.
import { ReactNode, createContext, useEffect, useState } from "react";
import { ThemeName, getTheme } from "../style/theme";
import { ThemeProvider } from "styled-components";
import { GlobalStyle } from "../style/global";
const DEFAULT_THEME_NAME = 'light';
const THEME_LOCAL_STORAGE_KEY = 'book_store_theme';
interface State {
themeName: ThemeName;
toggleTheme: () => void;
}
export const state = {
themeName: DEFAULT_THEME_NAME as ThemeName,
toggleTheme: () => { }
};
export const ThemeContext = createContext<State>(state);
...
ThemeContext.Provider의 value를 현재 테마 이름과 테마를 토글 할 수 있는 함수로 설정한다.
export const BookStoreThemeProvider = ({ children }: { children: ReactNode }) => {
const [themeName, setThemeName] = useState<ThemeName>('light');
const toggleTheme = () => {
setThemeName(themeName === 'light' ? 'dark' : 'light');
localStorage.setItem(
THEME_LOCAL_STORAGE_KEY,
themeName === 'light' ? 'dark' : 'light'
)
}
useEffect(() => {
const savedThemeName = localStorage.getItem(THEME_LOCAL_STORAGE_KEY) as ThemeName;
setThemeName(savedThemeName || DEFAULT_THEME_NAME);
}, [])
return (
<ThemeContext.Provider value={{ themeName, toggleTheme }}>
<ThemeProvider theme={getTheme(themeName)}>
<GlobalStyle themeName={themeName} />
{children}
</ThemeProvider>
</ThemeContext.Provider>
)
}
components/header/ThemeSwitcher.tsx
토글 할 수 있는 버튼은 App.tsx의 BookStoreThemeProvider의 하위 컴포넌트인 ThemeSwitcher에 존재한다.
버튼이 클릭되었을 때 테마가 바뀔 수 있도록 ThemeContext의 toggleTheme 함수를 onClick 이벤트에 지정한다.
import { useContext } from "react";
import { ThemeContext } from "../../context/themeContext";
function ThemeSwitcher() {
const { themeName, toggleTheme } = useContext(ThemeContext);
return (
<button onClick={toggleTheme}>
{themeName}
</button>
)
}
배운 점
- styled-components를 사용하니까 동적 스타일링이 편해지지만 js 파일이 복잡하고 styled/일반 컴포넌트의 구분이 어려웠다.
- contextAPI를 사용하여 테마를 변경하는 방법을 학습했다.
'데브코스' 카테고리의 다른 글
[14주차 주간 발표] CORS (0) | 2024.05.30 |
---|---|
[14주차 - DAY4] 도서 정보 사이트 - 라우팅 (0) | 2024.05.30 |
[14주차 - DAY1] 오픈 소스(5) (0) | 2024.05.27 |
[13주차 - DAY5] 오픈 소스(4) (0) | 2024.05.24 |
[13주차 - DAY4] 오픈 소스(3) (0) | 2024.05.23 |