styled-components 사용법 공부하기
props 사용해보기
const Button = styled.button`
/* Adapt the colors based on primary prop */
background: ${props => props.primary ? "palevioletred" : "white"};
color: ${props => props.primary ? "white" : "palevioletred"};
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
render(
<div>
<Button>Normal</Button>
<Button primary>Primary</Button>
</div>
);
버튼 컴포넌트의 props를 가져와서 primary인지 체크, 맞으면 색깔을 변경한다!
스타일 익스텐즈하기
위처럼 props를 가져와서 스타일을 변경하는 방법도 있지만, 이를 반복하는 것은 귀찮다.
다른 컴포넌트의 스타일을 상속하여 필요한 스타일만 추가하려면 styled()의 생성자에 해당 컴포넌트를 넣어줄 수 있다.
// The Button from the last section without the interpolations
const Button = styled.button`
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
`;
// A new component based on Button, but with some override styles
const TomatoButton = styled(Button)`
color: tomato;
border-color: tomato;
`;
render(
<div>
<Button>Normal Button</Button>
<TomatoButton>Tomato Button</TomatoButton>
</div>
);
또한 특정 컴포넌트를 동적으로 뭔가를 하고 싶을 때, as
를 사용하여 특정 컴포넌트만 대입하여 적용할 수 있다.
as에 넣을 수 있는 것
- 동적 역할 수행해주는 컴포넌트
- 스타일 태그
const Button = styled.button`
display: inline-block;
color: palevioletred;
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border: 2px solid palevioletred;
border-radius: 3px;
display: block;
`;
const ReversedButton = props => <Button {...props} children={props.children.split('').reverse()} />
render(
<div>
<Button>Normal Button</Button>
<Button as="a" href="#">Link with Button styles</Button>
<Button as={ReversedButton}>Custom Button with Normal Button styles</Button>
</div>
);
모든 컴포넌트 스타일링 가능
리액트의 보통 컴포넌트에서도 스타일을 바로 입혀 사용하는 것이 가능하다
// This could be react-router-dom's Link for example
const Link = ({ className, children }) => (
<a className={className}>
{children}
</a>
);
const StyledLink = styled(Link)`
color: palevioletred;
font-weight: bold;
`;
render(
<div>
<Link>Unstyled, boring Link</Link>
<br />
<StyledLink>Styled, exciting Link</StyledLink>
</div>
);
리액트 컴포넌트에서 props
스타일 컴포넌트 대상이 DOM 태그들인 경우, html속성들을 이용해서 DOM으로 전달한다. 리액트 컴포넌트의 경우 모든 props를 패스할 수 있다.
// Create an Input component that'll render an <input> tag with some styles
const Input = styled.input`
padding: 0.5em;
margin: 0.5em;
color: ${props => props.inputColor || "palevioletred"};
background: papayawhip;
border: none;
border-radius: 3px;
`;
// Render a styled text input with the standard input color, and one with a custom input color
render(
<div>
<Input defaultValue="@probablyup" type="text" />
<Input defaultValue="@geelen" type="text" inputColor="rebeccapurple" />
</div>
);
위에서 컴포넌트 스타일링할때 지정한 inputColor props의 경우 DOM에 전달되지 않지만, defaultValue, type 같은 표준 html 속성은 DOM에 전달된다.
즉, styled-components는 사용자 지정 props를 직접 걸러내어 DOM에 보내는 것이 가능하다.
스타일 컴포넌트 지정은 렌더링 되는 컴포넌트 바깥에서 하자
const Wrapper = ({ message }) => {
// WARNING: THIS IS VERY VERY BAD AND SLOW, DO NOT DO THIS!!!
const StyledWrapper = styled.div`
/* ... */
`
return <StyledWrapper>{message}</StyledWrapper>
}
위처럼 할 경우 속도가 매우 느려진다. 스타일 지정을 바깥에서 해서 사용하는 것이 좋다.
css의 의사 요소, 의사 선택자 및 네스팅
스타일드컴포넌트는 css 전처리기로 stylis
를 사용하는데, stylis
는 네스팅을 쓰기 위해 scss와 비슷한 구문을 지원한다.
엠퍼샌드(&)를 사용하여 해당 컴포넌트를 재참조할 수 있다.
const Thing = styled.div.attrs((/* props */) => ({ tabIndex: 0 }))`
color: blue;
&:hover {
color: red; // <Thing> when hovered
}
& ~ & {
background: tomato; // <Thing> as a sibling of <Thing>, but maybe not directly next to it
}
& + & {
background: lime; // <Thing> next to <Thing>
}
&.something {
background: orange; // <Thing> tagged with an additional CSS class ".something"
}
.something-else & {
border: 1px solid; // <Thing> inside another element labeled ".something-else"
}
`
render(
<React.Fragment>
<Thing>Hello world!</Thing>
<Thing>How ya doing?</Thing>
<Thing className="something">The sun is shining...</Thing>
<div>Pretty nice day today.</div>
<Thing>Don't you think?</Thing>
<div className="something-else">
<Thing>Splendid.</Thing>
</div>
</React.Fragment>
엠퍼샌드(&) 없이 셀렉터를 사용하면 해당 컴포넌트 아래에 있는 자식들을 참조한다.
const Thing = styled.div`
color: blue;
.something {
border: 1px solid; // an element labeled ".something" inside <Thing>
display: block;
}
`
render(
<Thing>
<label htmlFor="foo-button" className="something">Mystery button</label>
<button id="foo-button">What do I do?</button>
</Thing>
)
엠퍼샌드(&)를 잘 활용하면 글로벌로 걸려있는 스타일과 충돌이 일어날 수 있을 때에도 유용하게 처리할 수 있다.
const Thing = styled.div`
&& {
color: blue;
}
`
const GlobalStyle = createGlobalStyle`
div${Thing} {
color: red;
}
`
// 글로벌은 빨강색이라고 선언되어있지만, 파란색이 나온다
render(
<React.Fragment>
<GlobalStyle />
<Thing>
I'm blue, da ba dee da ba daa
</Thing>
</React.Fragment>
)
props 붙이기
props로는 정적인 값과 동적인 값 모두를 넣을 수 있다.
const Input = styled.input.attrs(props => ({
type: "text",
size: props.size || "1em",
}))`
border: 2px solid palevioletred;
margin: ${props => props.size};
padding: ${props => props.size};
`;
// Input's attrs will be applied first, and then this attrs obj
const PasswordInput = styled(Input).attrs({
type: "password",
})`
// similarly, border will override Input's border
border: 2px solid aqua;
`;
render(
<div>
<Input placeholder="A bigger text input" size="2em" />
<br />
{/* Notice we can still use the size attr from Input */}
<PasswordInput placeholder="A bigger password input" size="2em" />
</div>
);
styled-components
를 래핑할 때, attrs
요소들은 가장 안쪽에 있는 구성 요소부터 래핑한다.
그래서 위처럼 input 컴포넌트의 type을 text라고 지정해놓았지만, 해당 컴포넌트를 상속받아 type을 password로 재정의하면 해당 스타일컴포넌트의 type은 password로 재정의된다.
그래서 props의 size 어트리뷰트를 PasswordInput 컴포넌트는 그대로 사용할 수 있다.
css Props
styled-components
는 컴포넌트형으로 스타일을 만들지 않고 css라는 props로 스타일을 붙일 수도 있다.
<div
css={`
background: papayawhip;
color: ${props => props.theme.colors.text};
`}
/>
<Button
css="padding: 0.5em 1em;"
/>
// 위의 코드는 아래로 변환되어 작용한다.
import styled from 'styled-components';
const StyledDiv = styled.div`
background: papayawhip;
color: ${props => props.theme.colors.text};
`
const StyledButton = styled(Button)`
padding: 0.5em 1em;
`
<StyledDiv />
<StyledButton />
import를 하지 않아도 된다. 아래 바벨 플러그인을 설치했을 경우에!
npm install --save-dev babel-plugin-styled-components
{
"plugins": ["babel-plugin-styled-components"]
}