6. Global Style & Theme

  • Reset CSS

  • box-sizing ์†์„ฑ

  • word-break ์†์„ฑ

  • Theme

  • ThemeProvider

๊ฐ•์˜ ์ •๋ฆฌ

Reset CSS

๋ธŒ๋ผ์šฐ์ €๋“ค๋งˆ๋‹ค ์Šคํƒ€์ผ์ด ์ ์šฉ๋˜๋Š” ๋ฐฉ๋ฒ•์ด ๋‹ค ๋‹ฌ๋ผ์„œ ๋ชจ๋“  ์Šคํƒ€์ผ์„ ์ดˆ๊ธฐํ™”ํ•ด๋†“๊ณ , 0๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๊ธฐ ์œ„ํ•ด Reset CSS๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

ํŒจํ‚ค์ง€ ์„ค์น˜

npm i styled-reset

์ปดํฌ๋„ŒํŠธ ํ˜•ํƒœ๋กœ ์Šคํƒ€์ผ์„ ์ดˆ๊ธฐํ™”ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

import { Reset } from 'styled-reset';

export default function App() {
  return (
    <>
      <Reset />
      <Greeting />
    </>
  );
}

styled-components์—์„œ ์ „์—ญ ์Šคํƒ€์ผ์„ ์ง€์ •ํ•˜๋Š” ๋ฐฉ์‹

import { createGlobalStyle } from 'styled-components';

const GlobalStyle = createGlobalStyle`
	html {
		box-sizing: border-box;
	}
	
	*,
	*::before,
	*::after {
		box-sizing: inherit;
	}
	
	html {
		font-size: 62.5%;
	}
	
	body {
		font-size: 1.6rem;
	}
	
	:lang(ko) {
		h1, h2, h3 {
			word-break: keep-all;
		}
	}
`;

export default GlobalStyle;

Reset CSS์™€ ๋™์ผํ•˜๊ฒŒ App ์ปดํฌ๋„ŒํŠธ์— ์ ์šฉํ•ด์ค€๋‹ค.

import { Reset } from 'styled-reset';

import GlobalStyle from './styles/GlobalStyle';

export default function App() {
  return (
    <>
      <Reset />
      <GlobalStyle />
      <Greeting />
    </>
  );
}

Theme

๋””์ž์ธ ์‹œ์Šคํ…œ์˜ ๊ทผ๊ฐ„์„ ๋งˆ๋ จํ•˜๋Š”๋ฐ ํ™œ์šฉํ•œ๋‹ค. React Context๋ฅผ ์ด์šฉํ•˜์—ฌ ๋‚ด๋ ค์ค€๋‹ค.

const defaultTheme = {
  colors: {
    background: '#FFF',
    text: '#000',
    primary: '#F00',
    secondary: '#00F',
  },
};

export default defaultTheme;

ํƒ€์ž…์„ ์žก์•„์ฃผ๋Š”๊ฒŒ ์•ˆ์ •์ ์œผ๋กœ ์ž‘์„ฑํ•˜๊ธฐ์— ๋” ์ข‹๋‹ค.

const Theme = typeof defaultTheme;

export default Theme

App ์ปดํฌ๋„ŒํŠธ์—์„œ ThemeProvider๋กœ ๊ฐ์‹ธ์ค€๋‹ค.

import { ThemeProvider } from 'styled-components';

import { Reset } from 'styled-reset';

import defaultTheme from './styles/defaultTheme';

import GlobalStyle from './styles/GlobalStyle';

export default function App() {
  return (
    <ThemeProvider theme={defaultTheme}>
      <Reset />
      <GlobalStyle />
      <Greeting />
    </ThemeProvider>
  );
}

์‹ค์ œ๋กœ styled-components ๋‚ด๋ถ€์—์„œ theme์„ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•˜๋ฉด type์„ ์žก์ง€ ๋ชปํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š”๋ฐ type์„ ์žก์•„์ค˜์•ผ ํ•œ๋‹ค.

import 'styled-components';

import Theme from './Theme'

declare module 'styled-components' {
  export interface DefaultTheme extends Theme {}
}

usehooks-ts๋ฅผ ์ด์šฉํ•˜์—ฌ ๋‹คํฌ๋ชจ๋“œ ์ง€์›ํ•˜๊ธฐ.

import { useDarkMode } from 'usehooks-ts';

import { ThemeProvider } from 'styled-components';

import { Reset } from 'styled-reset';

import defaultTheme from './styles/defaultTheme';
import darkTheme from './styles/darkTheme';

import GlobalStyle from './styles/GlobalStyle';

import Greeting from './components/Greeting';
import Button from './components/Button';

export default function App() {
const { isDarkMode, toggle } = useDarkMode();

const theme = isDarkMode ? darkTheme : defaultTheme;

return (
  <ThemeProvider theme={theme}>
    <Reset />
    <GlobalStyle />
    <Greeting />
    <Button onClick={toggle} isActive={isDarkMode}>
      Dark Theme Toggle
    </Button>
  </ThemeProvider>
	);
}

defaultTheme์„ ํ•˜๋‚˜ ์žก์•„๋†“๊ณ , ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์—์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

Jest์—์„œ window.matchMedia ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด matchMedia๋ฅผ ์žก์•„์ฃผ๋ฉด ๋œ๋‹ค.

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