styled-components
ํ์ต ํค์๋
Styled Components
์ค์น ๋ฐ ์ค์
๊ธฐ๋ณธ ๋ฌธ๋ฒ
props
attrs
Reset CSS
Global Style
Theme
๐
๐ป styled-components
CSS in JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ
CSS์ ๋ฌธ์ ์ ์ ํด๊ฒฐํ๊ณ ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ก CSS๋ฅผ ์์ฑํ๋ ๋ฐฉ์์ผ๋ก ์ค๊ณ
Tagged Template Literals
๋ฌธ๋ฒ ์ฌ์ฉ
๐ Tagged Template Literals ES6์ ์ถ๊ฐ ๋ ๋ฌธ๋ฒ์ผ๋ก template literals ์ ๋์ฑ ๋ฐ์ ๋ ํ ํํ๋ก ํ๊ทธ๋ฅผ ์ฌ์ฉํ๋ฉด ํ ํ๋ฆฟ ๋ฆฌํฐ๋ด์ ํจ์๋ก ํ์ฑ ํ ์ ์๋ค. ํ๊ทธ ํจ์์ ์ฒซ๋ฒ์งธ ์ธ์๋ ๋ฌธ์์ด ๊ฐ์ ๋ฐฐ์ด์ ํฌํจํ๊ณ , ๋๋จธ์ง ์ธ์๋ ํํ์๊ด ๊ด๋ จ๋์ด ์๋ค.
โ๏ธ ์ค์น ๋ฐ ์ค์
ํจํค์ง ์ค์น
Babel Plugin์ SWC์์ ์ธ ์ ์๋๋ก ํฌํ ํ ๊ฒ๋ ํจ๊ป ์ค์น @swc/plugin-styled-components
โญ๏ธ SSR ์ง์ ๋ฑ์ ์ํ ๊ณต์ ๊ถ์ฅ์ฌํญ
npm i styled-components
npm i -D @types/styled-components @swc/plugin-styled-components
vscode-styled-components Extention ์ค์น
.swcrc
ํ์ผ ์์ฑ
{
"jsc": {
"experimental": {
"plugins": [
[
"@swc/plugin-styled-components",
{
"displayName": true,
"ssr": true
}
]
]
}
}
}
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
sytel.์ํ๋ ํ๊ทธ ์ฐ๊ณ ๋ฆฌํฐ๋๋ฐฉ์์ผ๋ก css ์์ฑ๋ค์ ๋ฃ์ด์ฃผ๋ฉด ๋๋ค.
import React from 'react';
import styled from 'styled-components';
const Circle = styled.div`
width: 5rem;
height: 5rem;
background: black;
border-radius: 50%;
`;
function App() {
return <Circle />;
}
export default App;
props
props๋ฅผ ์ด์ฉํด์ ํ์ฑํ ์ฌ๋ถ๋ฅผ ํํํ๊ฑฐ๋ ํน์ ์คํ์ผ์ ์ก์ ์ฃผ๊ณ ์ถ์ ๋ ์ ์ฉ
import { useState } from 'react';
import styled, { css } from 'styled-components';
type ButtonProps = {
active?: boolean;
};
const Button = styled.button<ButtonProps>`
width: 100px;
height: 100px;
background-color: #fff;
color: #000;
border: 1px solid gray;
${(props) =>
props.active &&
css`
background-color: #00f;
color: #fff;
border: 1px solid #00f;
`}
`;
export default function Switch() {
const [toggle, setToggle] = useState(false);
const handleClick = () => {
setToggle(!toggle);
};
return (
<Button onClick={handleClick} active={toggle}>
ON/OFF
</Button>
);
}
attrs
๊ธฐ๋ณธ ์์ฑ์ ์ถ๊ฐํ ์ ์๋ค. ๋ถํ์ํ๊ฒ ๋ฐ๋ณต๋๋ ์์ฑ์ ์ฒ๋ฆฌํ ๋ ์ ์ฉ
๋ฒํผ์ ๋ง๋ค ๋ ์ ๊ทน์ ์ผ๋ก ํ์ฉ
import styled, { css } from 'styled-components';
type ButtonProps = {
type?: 'button' | 'submit' | 'reset';
};
const Button = styled.button.attrs<ButtonProps>((props) => {
return {
type: props.type ?? 'button',
};
})`
width: 100px;
height: 100px;
background-color: #fff;
color: #000;
border: 1px solid gray;
`;
export default Button
Reset CSS
styled-reset ํจํค์ง ์ค์น
styled-components ์์ Reset CSS ์ฒ๋ผ ํ๊ทธ๋ค์ ๊ธฐ๋ณธ ์์ฑ์ ์ด๊ธฐํ ํ๊ธฐ ์ํด ์ค์น
npm i styled-reset
์ต์์ ์ปดํฌ๋ํธ ์ ์ฉ
// App.tsx
import { Reset } from 'styled-reset';
export default function App() {
return (
<div>
<Reset />
</div>
);
}
Global Style
styles
ํด๋ ,GlobalStyle.ts
ํ์ผ ์์ฑ
mkdir styles
touch styles/GlobalStyle.ts
createGlobalStyle ์ฌ์ฉํด์ ์ ์ญ ์คํ์ผ ์ง์
// GlobalStyle.ts
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
์ต์์ ์ปดํฌ๋ํธ ์ ์ฉ
import { Reset } from 'styled-reset';
import GlobalStyle from '../styles/GlobalStyle';
export default function App() {
return (
<div>
<Reset />
<GlobalStyle />
</div>
);
}
Theme
Theming โ
<ThemeProvider>
ํ์ฉ๋์์ธ ์์คํ ์ ๊ทผ๊ฐ์ ๋ง๋ จํ๋ ํ์ฉ
ex.๋คํฌ๋ชจ๋ ๋์ํ๊ธฐ ์ฌ์
styles/defaultTheme.ts
ํ์ผ ์์ฑ
defaultTheme.ts
ํ์ผ ์์ฑconst defaultTheme = {
colors: {
background: '#FFF',
text: '#000',
primary: '#F00',
secondary: '#00F',
},
};
export default defaultTheme;
์ญ์ผ๋ก typeof
๋ฅผ ์ฌ์ฉํด์ Theme ํ์
์ง์ โ styles/Theme.ts
ํ์ผ ์์ฑ
typeof
๋ฅผ ์ฌ์ฉํด์ Theme ํ์
์ง์ โ styles/Theme.ts
ํ์ผ ์์ฑimport defaultTheme from './defaultTheme';
type Theme = typeof defaultTheme;
export default Theme;
styles/darkTheme.ts
ํ์ผ ์์ฑํด์ Theme ์ผ๋ก ํ์
์ง์
darkTheme.ts
ํ์ผ ์์ฑํด์ Theme ์ผ๋ก ํ์
์ง์ import Theme from './Theme';
const darkTheme : Theme = {
colors: {
background: '#000',
text: '#FFF',
primary: '#F00',
secondary: '#00F',
},
};
export default darkTheme;
์ต์์ <ThemeProvider>
์ปดํฌ๋ํธ๋ฅผ ํตํด ๊ณต๊ธ
<ThemeProvider>
์ปดํฌ๋ํธ๋ฅผ ํตํด ๊ณต๊ธimport { Reset } from 'styled-reset';
import { ThemeProvider } from 'styled-components';
import GlobalStyle from '../styles/GlobalStyle';
import defaultTheme from '../styles/defaultTheme';
import Greeting from './components/Greeting';
import Switch from './components/Switch';
export default function App() {
const theme = defaultTheme;
return (
<ThemeProvider theme={theme}> {/* ๐๐ป Theme ๊ณต๊ธ */}
<Reset />
<GlobalStyle />
<Greeting />
<Switch />
</ThemeProvider>
);
}
๊ณต๊ธ ๋ฐ์ theme์ ์ฌ์ฉํ๊ธฐ ์ํด์ ํ์
์ง์ ํ์ โ styled.d.ts
ํ์ผ ์์ฑ
styled.d.ts
ํ์ผ ์์ฑ์๋ ์ฝ๋ ์ฒ๋ผ ํ๋์ฉ ์ง์ ํด์ค๋ ๋์ง๋ง, ์์ฑ๋ค์ด ์ถ๊ฐ๋ ๊ฐ๋ฅ์ฑ์ ๊ณ ๋ คํด์
Theme.ts
ํ์ฉ
import 'styled-components';
declare module 'styled-components' {
export interface DefaultTheme extends Theme {
colors: {
background: string;
text: string;
primary: string;
secondary: string;
}
}
}
๋๋
import 'styled-components';
import type Theme from './Theme';
declare module 'styled-components' {
export interface DefaultTheme extends Theme {}
}
๐ Theme ์๋ฏธ ์๊ฒ ์ฌ์ฉํ๊ธฐ ์ํด ์ฐธ๊ณ
๐จ window.matchMedia
Error ๋ฐ์
window.matchMedia
Error ๋ฐ์Jest ํ ์คํธ์์ ๋คํฌ๋ชจ๋ ๊ตฌํ์
window.matchMedia
Error ๋ฐ์
window.matchMedia
๋ ์ฌ์ฉ์์ ์์คํ ์ค์ ์ ํ ๋ง๋ฅผ ์ธ์ํ๊ธฐ ์ํด ์ฌ์ฉํ๋ค.
Jest ๊ณต์๋ฌธ์์๋ src/setupTests.ts
ํ์ผ ํด๋น ์ฝ๋ ์ฌ์ฉํด์ ์ด์๋ฅผ ํด๊ฒฐํ๋ผ๊ณ ์๋ดํ๊ณ ์๋ค.
// src/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