Global Style & Theme
Global Style
์ ์ญ์ผ๋ก ์คํ์ผ์ ์ง์
createGlobalStyle
์ ์ญ ์คํ์ผ์ ์ฒ๋ฆฌํ๋ ํน๋ณํ ์คํ์ผ๋ ์ปดํฌ๋ํธ๋ฅผ ์์ฑํ๋ ํฌํผ ํจ์
์ผ๋ฐ์ ์ธ ์คํ์ผ๋ ์ปดํฌ๋ํธ๋ ์๋์ผ๋ก ํด๋์ค ๋ฒ์๊ฐ ์ง์ ๋์ด ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ก๋ถํฐ ๊ฒฉ๋ฆฌ๋์ง๋ง createGlobalStyle
์ ์ด๋ฌํ ์ ํ์ด ์์ด์ ์ ์ญ ์คํ์ผ๋ง์ด ๊ฐ๋ฅํ๋ค.
์ ์ญ ์คํ์ผ์ reset css๋ฅผ ์ถ๊ฐํ์ฌ ์ด๊ธฐํ๋ ๊ฐ๋ฅํ๋ค.
// styles/GlobalStyle.ts
import { createGlobalStyle } from 'styled-components';
import reset from 'styled-reset';
const GlobalStyle = createGlobalStyle`
${reset}
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
html {
// ๋ธ๋ผ์ฐ์ ์ ํฐํธ ํฌ๊ธฐ ์ค์ ๊ณผ ์ฐ๋๋๋ rem ๋จ์๋ฅผ ์ฌ์ฉ,
// rem ๋จ์๋ฅผ ๊ณ์ฐํ๊ธฐ ์ฝ๊ฒ ํ๊ธฐ ์ํด ๊ธฐ๋ณธ ํฐํธ๋ฅผ 62.5%(10px)๋ก ์ค์ ํ๋ค.
font-size: 62.5%; // 10px, (100% === 16px)
}
body {
font-size: 1.6rem;
}
:lang(ko) {
// ํ๊ธ๋ก ์์ฑ๋ ์์ ์ค ํค๋ฉ ํ๊ทธ์ ์๋์น ์์ ์ค๋ฐ๊ฟ์ ๋ฐฉ์งํ๊ธฐ ์ํด ์ค์
h1, h2, h3 {
word-break: keep-all;
}
}
`;
export default GlobalStyle;
import GlobalStyle from './styles/GlobalStyle';
export default function App() {
return (
<>
<GlobalStyle />
<Greeting />
</>
);
}
Theme
๋์์ธ ์์คํ ์ ์ฒด์์ ์ฌ์ฉ๋๋ ์์, ํ์ดํฌ๊ทธ๋ํผ, ์์ด์ฝ ๋ฑ์ ๋์์ธ ์์์ ์งํฉ
ํ ๋ง๋ฅผ ์ ์ํ ๋ ๋์์ธ ์์คํ ์์ ์ฌ์ฉ๋๋ ์ปฌ๋ฌ ํ๋ ํธ, ํ์ดํฌ๊ทธ๋ํผ, ๊ฐ๊ฒฉ ๋ฑ์ ํ ํฐ์ผ๋ก ๊ด๋ฆฌํ๊ณ , ์ด๋ฅผ ์ด์ฉํด์ ๋์์ธ ์์คํ ์ ์ฒด์ ์ผ๊ด์ฑ ์๋ ๋์์ธ์ ์ ์งํ๊ณ ์์ ์ ํจ์จ์ฑ์ ๋ํ๋ค.
Token Naming
๋์ ๋ณด์ด๋ ๋จํธ์ ์ธ ์ ๋ณด๋ฅผ ๋์ด์, โ์๋ฏธโ์ ์ง์ค
๋์์ธ ์์คํ ์ฌ์ฉ์๊ฐ ํน์ ํ ํฐ์ ๋์์ธ ์๋์ ์ฌ์ฉ๋ฒ์ ์ดํดํ ์ ์๋๋ก ํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ค์ํ๋ค.
์ด๋ฌํ ์ฒ ํ์ ์๋์ ๋ฐ๋ฅธ ๋์์ธ์ ๊ฐ๋ฅ์ผ ํ๋ค.
์ปฌ๋ฌ ํ ํฐ์ ์๋ก ๋ค๋ฉด ์์ ๊ฐ์ ์ฝ๋งค์ด์ง ์๊ณ ์๋๊ฐ ๋ ์ ๋ด๊ธด ์ถ์ํ๋ ๋ค์ด๋ฐ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค,
์ถํ ์์๊ฐ์ด ๋ณ๊ฒฝ๋๋ ์ํฉ์ด ์๊ฒจ๋ ์ฝ๋ ๋ณ๊ฒฝ์ด ์ฝ๊ณ , ์ ์ง๋ณด์์ฑ์ด ๋๋ค.
ํ์ง๋ง ์ด๋ค ๊ฒฝ์ฐ๋ ์๋๊ฐ ๋ด๊ธด ๋ค์ด๋ฐ์ ์ฌ์ฉํ๊ธฐ ์ด๋ ค์ธ ์๋ ์๋ค. ์ค์ํ๊ฑด ์ผ๊ด์ฑ๊ณผ ๊ฐ๋ ์ฑ์ด๋ค.
Theme Provider
์คํ์ผ๋ ์ปดํฌ๋ํธ์์ ์ ๊ณต๋๋ ThemeProvider๋ ๋ด๋ถ์ ์ผ๋ก Context API๋ฅผ ์ฌ์ฉํ์ฌ ์์๋ค์๊ฒ ํ ๋ง ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํ๋ค.
import { ThemeProvider } from 'styled-components';
import { GlobalStyle, theme } from '@/styles';
const App = () => (
<ThemeProvider theme={theme}>
<GlobalStyle />
<Greeting />
</ThemeProvider>
);
export default App;
ํ
๋ง ํ์
์ ์ธ ๋ณํฉ
์คํ์ผ ์ปดํฌ๋ํธ ํ์ ์ ์ ํ์ผ์ ์ ์ธ๋ ํ ๋ง ์ธํฐํ์ด์ค ํ์ ์ ์ฌ์ฉ์๊ฐ ๋ง๋ ํ ๋ง์ ํ์ ์ผ๋ก ํ์ฅํ๋ ค๋ฉด, ์ ์ธ ๋ณํฉ์ ์ฌ์ฉํด์ผ ํ๋ค.
์ฌ์ฉ์๊ฐ ์ ์ํ ํ ๋ง์์ ์ญ์ผ๋ก ํ์ ์ ์ถ์ถํด์ ํ์ฅํ๋ฉด ํธ-ํ๋ค.
styled.d.ts
styled.d.ts
import 'styled-components';
import { defaultTheme } from '@/styles';
type Theme = typeof defaultTheme;
declare module 'styled-components' {
export interface DefaultTheme extends Theme {}
}
DarkMode ๊ตฌํํ๊ธฐ
ํ ๋ง๋ฅผ ๊ฐ์๋ผ์ฐ๋ ์์ผ๋ก ๋คํฌ๋ชจ๋๋ฅผ ๊ตฌํ ๊ฐ๋ฅ
useDarkTheme
useDarkTheme
import { useLocalStorage, useMediaQuery, useUpdateEffect } from 'usehooks-ts'
const COLOR_SCHEME_QUERY = '(prefers-color-scheme: dark)'
interface UseDarkModeOutput {
isDarkMode: boolean
toggle: () => void
enable: () => void
disable: () => void
}
function useDarkMode(defaultValue?: boolean): UseDarkModeOutput {
const isDarkOS = useMediaQuery(COLOR_SCHEME_QUERY)
const [isDarkMode, setDarkMode] = useLocalStorage<boolean>(
'usehooks-ts-dark-mode',
defaultValue ?? isDarkOS ?? false,
)
// Update darkMode if os prefers changes
useUpdateEffect(() => {
setDarkMode(isDarkOS)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isDarkOS])
return {
isDarkMode,
toggle: () => setDarkMode(prev => !prev),
enable: () => setDarkMode(true),
disable: () => setDarkMode(false),
}
}
export default useDarkMode
export default function App() {
const { isDarkMode, toggle } = useDarkMode();
const theme = isDarkMode ? darkTheme : defaultTheme;
return (
<ThemeProvider theme={theme}>
<GlobalStyle />
<Greeting />
<Button onClick={toggle}>
Dark Theme Toggle
</Button>
</ThemeProvider>
);
}
jest 'window.matchMedia
' ์ค๋ฅ
window.matchMedia
' ์ค๋ฅjest์์ ์ฌ์ฉ๋๋ jsdom์์ ๊ตฌํ๋์ง ์์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ๋ ์ค๋ฅ๋ฅผ ๋ฐ์์ํจ๋ค.
์ด ๊ฒฝ์ฐ ๋ชจํน์ ํตํด ํด๊ฒฐ์ด ๊ฐ๋ฅํ๋ค
setupTests.ts
setupTests.ts
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation((query) => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
Last updated