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
import 'styled-components';
import { defaultTheme } from '@/styles';
type Theme = typeof defaultTheme;
declare module 'styled-components' {
export interface DefaultTheme extends Theme {}
}
DarkMode ꡬννκΈ°
ν
λ§λ₯Ό κ°μλΌμ°λ μμΌλ‘ λ€ν¬λͺ¨λλ₯Ό ꡬν κ°λ₯
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>
);
}
μ΄ κ²½μ° λͺ¨νΉμ ν΅ν΄ ν΄κ²°μ΄ κ°λ₯νλ€
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(),
})),
});