νμ΅ ν€μλ
π
π»
CSS in JS λΌμ΄λΈλ¬λ¦¬
CSSμ λ¬Έμ μ μ ν΄κ²°νκ³ μ μλ°μ€ν¬λ¦½νΈ μ½λλ‘ CSSλ₯Ό μμ±νλ λ°©μμΌλ‘ μ€κ³
Tagged Template Literals
λ¬Έλ² μ¬μ©
π
ES6μ μΆκ° λ λ¬Έλ²μΌλ‘ template literals μ λμ± λ°μ λ ν ννλ‘ νκ·Έλ₯Ό μ¬μ©νλ©΄ ν
νλ¦Ώ 리ν°λ΄μ ν¨μλ‘ νμ± ν μ μλ€. νκ·Έ ν¨μμ 첫λ²μ§Έ μΈμλ λ¬Έμμ΄ κ°μ λ°°μ΄μ ν¬ν¨νκ³ , λλ¨Έμ§ μΈμλ ννμκ΄ κ΄λ ¨λμ΄ μλ€.
βοΈ μ€μΉ λ° μ€μ
ν¨ν€μ§ μ€μΉ
βοΈ SSR μ§μ λ±μ μν 곡μ κΆμ₯μ¬ν
npm i styled-components
npm i -D @types/styled-components @swc/plugin-styled-components
vscode-styled-components Extention μ€μΉ
{
"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
μ΅μμ μ»΄ν¬λνΈ μ μ©
// 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
// 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
λμμΈ μμ€ν
μ κ·Όκ°μ λ§λ ¨νλ νμ©
ex.λ€ν¬λͺ¨λ λμνκΈ° μ¬μ
styles/defaultTheme.ts
νμΌ μμ±
const defaultTheme = {
colors: {
background: '#FFF',
text: '#000',
primary: '#F00',
secondary: '#00F',
},
};
export default defaultTheme;
μμΌλ‘ typeof
λ₯Ό μ¬μ©ν΄μ Theme νμ
μ§μ β styles/Theme.ts
νμΌ μμ±
import defaultTheme from './defaultTheme';
type Theme = typeof defaultTheme;
export default Theme;
styles/darkTheme.ts
νμΌ μμ±ν΄μ Theme μΌλ‘ νμ
μ§μ
import Theme from './Theme';
const darkTheme : Theme = {
colors: {
background: '#000',
text: '#FFF',
primary: '#F00',
secondary: '#00F',
},
};
export default darkTheme;
μ΅μμ <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
νμΌ μμ±
μλ μ½λ μ²λΌ νλμ© μ§μ ν΄μ€λ λμ§λ§, μμ±λ€μ΄ μΆκ°λ κ°λ₯μ±μ κ³ λ €ν΄μ 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 μλ―Έ μκ² μ¬μ©νκΈ° μν΄ μ°Έκ³
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(),
})),
});
π μ°Έκ³