node-sass 라이브러리 설치
$blue: #228be6; // ⭐css 값을 변수로 선언할 수 있습니다.⭐
.Button {
...
background: $blue; // 주석 사용
&:hover {
background: lighten($blue, 10%); // 색상 10% 밝게
}
&:active {
background: darken($blue, 10%); // 색상 10% 어둡게
}
}
className={['Button', size].join(' ')}
className={`Button ${size}`}
classnames 라이브러리 사용하여 손쉽게 문자열을 조합 할 수 있습니다.
classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'
classNames(['foo', 'bar']); // => 'foo bar'
mixin을 사용하여 반복되는 코드들을 재사용할 수 있습니다.
$blue: #228be6;
$gray: #495057;
$pink: #f06595;
//button-color라는 mixin을 만들고
@mixin button-color($color) {
background: $color;
&:hover {
background: lighten($color, 10%);
}
&:active {
background: darken($color, 10%);
}
}
.Button {
...
// 사이즈 관리
&.large {
...
}
&.medium {
...
}
&.small {
...
}
// 색상 관리
&.blue {
@include button-color($blue);
}
&.gray {
@include button-color($gray);
}
&.pink {
@include button-color($pink);
}
//@include 를 사용하여 button-color라는 믹스인을 사용
& + & {
margin-left: 1rem;
}
}
//Button.js
import React from 'react';
import classNames from 'classnames';
import './Button.scss';
function Button({ children, size, color, outline, fullWidth }) {
return (
<button
className={classNames('Button', size, color, { outline, fullWidth })}
> // outline, fullWidth 옵션 추가
{children}
</button>
);
}
Button.defaultProps = {
size: 'medium',
color: 'blue'
};
export default Button;
//Button.scss
$blue: #228be6;
$gray: #495057;
$pink: #f06595;
@mixin button-color($color) {
background: $color;
&:hover {
background: lighten($color, 10%);
}
&:active {
background: darken($color, 10%);
}
&.outline {
...
border: 1px solid $color;
}
}
.Button {
...
// 사이즈 관리
&.large {
...
}
&.medium {
...
}
&.small {
...
}
// 색상 관리
&.blue {
@include button-color($blue);
}
&.gray {
@include button-color($gray);
}
&.pink {
@include button-color($pink);
}
& + & {
margin-left: 1rem;
}
&.fullWidth {
...
}
}
//App.js
import React from 'react';
import './App.scss';
import Button from './components/Button';
function App() {
return (
<div className="App">
<div className="buttons">
<Button size="large">BUTTON</Button> //large, blue(default)
<Button>BUTTON</Button> //medium(default), blue(default)
<Button size="small">BUTTON</Button> //small, blue(default)
</div>
<div className="buttons">
<Button size="large" color="gray"> //large, gray
BUTTON
</Button>
<Button color="gray">BUTTON</Button> //medium(default), gray
<Button size="small" color="gray"> //small, gray
BUTTON
</Button>
</div>
<div className="buttons">
<Button size="large" color="pink"> //large, pink
BUTTON
</Button>
<Button color="pink">BUTTON</Button> //medium(default), pink
<Button size="small" color="pink">
BUTTON
</Button> //small, pink
</div>
<div className="buttons">
<Button size="large" color="blue" outline>
BUTTON
</Button> //large, blue, outline(option)
<Button color="gray" outline>
BUTTON
</Button> //medium(default), gray, outline(option)
<Button size="small" color="pink" outline>
BUTTON
</Button> //small, pink, outline(option)
</div>
<div className="buttons">
<Button size="large" fullWidth>
BUTTON
</Button>
<Button size="large" fullWidth color="gray">
BUTTON
</Button> //large, gray, fullWidth(option)
<Button size="large" fullWidth color="pink">
BUTTON
</Button> //large, pink, fullWidth(option)
</div>
</div>
);
}
export default App;
function Button({ children, size, color, outline, fullWidth, ...rest }) {
return (
<button
className={classNames('Button', size, color, { outline, fullWidth })}
{...rest}
>
{children}
</button>
);
}
function App() {
return (
<Button size="large" onClick={() => console.log('클릭됐다!')}>
BUTTON
</Button>)
}
컴포넌트가 어떤 props 를 받을 지 확실치는 않지만 그대로 다른 컴포넌트 또는 HTML 태그에 전달을 해주어야 하는 상황에 ...rest 문법을 활용!
리액트 프로젝트에서 컴포넌트를 스타일링 할 때 CSS Module 이라는 기술을 사용하면, CSS 클래스가 중첩되는 것을 완벽히 방지할 수 있습니다.
//사용법
import styles from "./Box.module.css";
function Box() {
return <div className={styles.Box}>{styles.Box}</div>;
}
클래스 이름에 - 가 들어가 있을때 : styles['my-class']
클래스 이름이 여러개일때 : ${styles.one} ${styles.two}
조건부 스타일링을 해야 할 때 : ${styles.one} ${condition ? styles.two : ''}
👉 classnames 라이브러리의 bind 기능으로 CSS Module을 더 간편하게 쓸 수 있습니다.
//classnames라이브러리의 bind 사용법
import styles from './CheckBox.module.css';
import classNames from 'classnames/bind';
const cx = classNames.bind(styles);
function CheckBox({ children, checked, ...rest }) {
return (
<div className={cx('checkbox')}>
...
cx('one', 'two')
cx('my-component', {
condition: true
})
cx('my-component', ['another', 'classnames'])
✅ CSS Module 을 사용하고 있는 파일에서 클래스 이름을 고유화 하지 않고 전역적 클래스이름을 사용하고 싶을 때
:global .my-global-name {
}
// Sass를 사용할때
:global {
.my-global-name {
}
}
✅ CSS Module 을 사용하지 않는 곳에서 특정 클래스에서만 고유 이름을 만들어서 사용하고 싶을 때
:local .make-this-local {
}
:local {
.make-this-local {
}
}
styled-components 는 현존하는 CSS in JS 관련 리액트 라이브러리 중에서 가장 인기 있는 라이브러리입니다. 이에 대한 대안으로는 emotion 와 styled-jsx가 있습니다.
import styled from 'styled-components';
const Circle = styled.div`
...
background: ${props => props.color || 'black'};
`; //props설정에 따라 스타일링을 할 수 있다.
function App() {
return <Circle color="blue" />;
}
import styled, { css } from 'styled-components';
const Circle = styled.div`
...
background: ${props => props.color || 'black'};
${props =>
props.huge &&
css`
width: 10rem;
height: 10rem;
`}
`; //특정 props가 있을 때에만 설정하는 css
function App() {
return <Circle color="red" huge />;
}
import styled from 'styled-components';
import { darken, lighten } from 'polished';
const StyledButton = styled.button`
/* 공통 스타일 */
...
/* 크기 */
...
/* 색상 */
background: #228be6;
&:hover {
background: ${lighten(0.1, '#228be6')};
}
&:active {
background: ${darken(0.1, '#228be6')};
}
/* 기타 */
...
`;
function Button({ children, ...rest }) {
return <StyledButton {...rest}>{children}</StyledButton>;
}
export default Button;
import styled, { ThemeProvider } from 'styled-components';
import Button from './components/Button';
const AppBlock = styled.div`
...
`;
function App() {
return (
<ThemeProvider
theme={{
palette: {
blue: '#228be6',
gray: '#495057',
pink: '#f06595'
}
}}
>
<AppBlock>
<Button>BUTTON</Button>
<Button color="gray">BUTTON</Button>
<Button color="pink">BUTTON</Button>
</AppBlock>
</ThemeProvider>
);
}
import styled, { css } from 'styled-components';
import { darken, lighten } from 'polished';
const StyledButton = styled.button`
/* 공통 스타일 */
...
/* 크기 */
...
/* 색상 */
${props => {
const selected = props.theme.palette[props.color];
return css`
background: ${selected};
&:hover {
background: ${lighten(0.1, selected)};
}
&:active {
background: ${darken(0.1, selected)};
}
`;
}}
// ${({ theme, color }) => {
// const selected = theme.palette[color];
// return css`...
// => 이렇게 비구조화 할당도 사용!
/* 기타 */
& + & {
margin-left: 1rem;
}
`;
function Button({ children, ...rest }) {
return <StyledButton {...rest}>{children}</StyledButton>;
}
Button.defaultProps = {
color: 'blue'
};
위 로직은 색상관련 코드를 분리하여 사용할 수 있습니다.
import styled, { css } from 'styled-components';
import { darken, lighten } from 'polished';
const colorStyles = css`
${({ theme, color }) => {
const selected = theme.palette[color];
return css`
background: ${selected};
&:hover {
background: ${lighten(0.1, selected)};
}
&:active {
background: ${darken(0.1, selected)};
}
`;
}}
`;
// 색상 관련 스타일코드 따로 분리
const StyledButton = styled.button`
/* 공통 스타일 */
...
/* 크기 */
...
/* 색상 */
${colorStyles} //따로 분리한 색상 관련 코드
/* 기타 */
& + & {
margin-left: 1rem;
}
`;
function Button({ children, color, ...rest }) {
return <StyledButton color={color} {...rest}>{children}</StyledButton>;
}
Button.defaultProps = {
color: 'blue'
};
import styled, { css } from 'styled-components';
import { darken, lighten } from 'polished';
const colorStyles = css`
${({ theme, color }) => {
기존 색상 관련 코드들...
}}
`;
//사이즈 관련 코드도 추가!!
const sizeStyles = css`
${props =>
props.size === 'large' &&
css`
height: 3rem;
font-size: 1.25rem;
`}
${props =>
props.size === 'medium' &&
css`
height: 2.25rem;
font-size: 1rem;
`}
${props =>
props.size === 'small' &&
css`
height: 1.75rem;
font-size: 0.875rem;
`}
`;
const StyledButton = styled.button`
/* 공통 스타일 */
...
/* 크기 */
${sizeStyles}
/* 색상 */
${colorStyles} //따로 분리한 사이즈 관련 코드
/* 기타 */
& + & {
margin-left: 1rem;
}
`;
function Button({ children, color, size, ...rest }) {
return (
<StyledButton color={color} size={size} {...rest}>
{children}
</StyledButton>
);
}
Button.defaultProps = {
color: 'blue',
size: 'medium'
};
위 sizeStyles의 중복되는 코드 리팩토링하기
const sizes = {
large: {
height: '3rem',
fontSize: '1.25rem'
},
medium: {
height: '2.25rem',
fontSize: '1rem'
},
small: {
height: '1.75rem',
fontSize: '0.875rem'
}
};
const sizeStyles = css`
${({ size }) => css`
height: ${sizes[size].height};
font-size: ${sizes[size].fontSize};
`}
`;
import styled, { css } from 'styled-components';
import { darken, lighten } from 'polished';
const colorStyles = css`
${({ theme, color }) => {
const selected = theme.palette[color];
return css`
...
${props =>
props.outline &&
css`
...
`} //outline props가 추가됐을때의 색상관련 코드들
`;
}}
`;
const sizes = {
...
};
const sizeStyles = css`
...
`;
//fullWidthStyle관련 코드 추가!!
const fullWidthStyle = css`
${props =>
props.fullWidth &&
css`
...
`}
`;
const StyledButton = styled.button`
/* 공통 스타일 */
...
/* 크기 */
${sizeStyles}
/* 색상 */
${colorStyles}
/* 기타 */
...
${fullWidthStyle} //따로 분리한 fullWidth관련 코드
`;
function Button({ children, color, size, outline, fullWidth, ...rest }) {
return (
<StyledButton
color={color}
size={size}
outline={outline}
fullWidth={fullWidth}
{...rest}
>
{children}
</StyledButton>
);
}
Button.defaultProps = {
color: 'blue',
size: 'medium'
};
import styled from 'styled-components';
import Button from './Button';
const ShortMarginButton = styled(Button)`
& + & {
margin-left: 0.5rem;
}
`; //⭐Button컴포넌트에 스타일을 덮어쓰기
function Dialog({ title, children, confirmText, cancelText }) {
return (
<>
........
<ShortMarginButton color="gray">{cancelText}</ShortMarginButton>
<ShortMarginButton color="pink">{confirmText}</ShortMarginButton>
</>
);
}
Dialog.defaultProps = {
confirmText: '확인',
cancelText: '취소'
};