리액트 컴포넌트 스타일링 : Sass

1. Sass(Syntactically Awesome Stlye Sheets)

1-1. Sass 장점

  • 복잡한 작업을 쉽게 할 수 있음
  • 코드의 재활용성을 높여줌
  • 코드의 가독성을 높임 -> 쉬운 유지보수

1-2. Sass 확장자

  • .scss/.sass 지원

sass와 scss 비교

sass

$font-stack:    Helvetica, sans-serif
$primary-color: #333

body
  font: 100% $font-stack
  color: $primary-color

scss

$font-stack:    Helvetica, sans-serif;
$primary-color: #333;

body {
  font: 100% $font-stack;
  color: $primary-color;
}

2. Sass 시작

2-1. 라이브러리 설치

  • yarn add node-sass

2-2. button.js 생성

  • scss 적용할 button의 틀 만들기
import React from 'react'
import './Button.scss'

function Button({ children }) {
  return <button className="Button">{children}</button>
}

export default Button

2-3. button.scss 파일 생성

  • components 파일 아래에 Button.scss 생성
$blue: #228be6; // 주석 선언

.Button {
  color: white;
  font-weight: bold;
  outline: none;
  border-radius: 4px;
  cursor: pointer;

  height: 2.25rem;
  padding-left: 1rem;
  padding-right: 1rem;
  font-size: 1rem;

  background: $blue; // 주석 사용
  &:hover {
    background: lighten($blue, 10%); // 색상 10% 밝게
  }

  &:active {
    background: darken($blue, 10%); // 색상 10% djenqrp
  }
}
  • $blue: #228be6;와 같은 스타일 파일에 사용할 수 있는 변수 선언
  • lighten()/darken() : 더 밝게/어둡게 함수

2-4. Button 프레임 적용

  • App.js 파일에 Button.js 사용하기
import './App.css'
import Button from './components/Button'

function App() {
  return (
    <div className="App">
      <div className="buttons">
        <Button>BUTTON</Button>
      </div>
    </div>
  )
}

export default App

3. 버튼 사이즈 조정

  • 버튼크기에 large, medium, small 설정
  • Button.js에서 defaultProps 통해 size의 기본값을 medium으로 설정
  • 이 값은 button의 className에 넣음

Button.js

import React from 'react'
import './Button.scss'

function Button({ children, size }) {
  return <button className={['Button', size].join(' ')}>{children}</button>
}

Button.defaultProps = {
  size: 'medium',
}

export default Button

className에 css 클래스 이름 설정

  • css 클래스 이름을 동적으로 넣음
    className={['Button', size].join(' ')}
  • 다른 방법
    classNmae={`Button ${size}`}

classnames 라이브러리 사용

  • 위에 조건부로 css 클래스 넣어 문자열을 직접 조합하는 것보다 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'

// 동시에 여러개의 타입으로 받아올 수 도 있습니다.
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'

// false, null, 0, undefined 는 무시됩니다.
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'

classNames 라이브러리 설치

$ npm install classnames

js 파일에 classNames 사용

import classNames from 'classnames'
import React from 'react'
import './Button.scss'

function Button({ children, size }) {
  return <button className={classNames('Button', size)}>{children}</button>
}

Button.defaultProps = {
  size: 'medium',
}

export default Button
  • props 값이 button 태그의 className으로 전달
  • Button.scss에서 다른 크기를 지정

Button.scss에 크기 설정

$blue: #228be6; // 주석 선언

.Button { 
  display: inline-flex;
  justify-content: center;
  align-items: center;
  color: white;
  font-weight: bold;
  outline: none;
  border-radius: 4px;
  cursor: pointer;

  // 사이즈 관리
  &.large {
    height: 3rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1.25rem;
  }

  &.medium {
    height: 2.25rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1rem;
  }

  &.small {
    height: 1.75rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 0.875rem;
  }

  background: $blue; // 주석 사용
  &:hover {
    background: lighten($blue, 10%); // 색상 10% 밝게
  }

  &:active {
    background: darken($blue, 10%); // 색상 10% djenqrp
  }
}

다른 크기의 버튼 사용하기

import './App.css'
import Button from './components/Button'

function App() {
  return (
    <div className="App">
      <div className="buttons">
        <Button size="large">BUTTON</Button>
        <Button>BUTTON</Button>
        <Button size="small">BUTTON</Button>
      </div>
    </div>
  )
}

export default App

버튼과 버튼 사이에 여백

$blue: #228be6; // 주석 선언

.Button {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  color: white;
  font-weight: bold;
  outline: none;
  border-radius: 4px;
  cursor: pointer;

  // 사이즈 관리
  &.large {
    height: 3rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1.25rem;
  }

  &.medium {
    height: 2.25rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1rem;
  }

  &.small {
    height: 1.75rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 0.875rem;
  }

  background: $blue; // 주석 사용
  &:hover {
    background: lighten($blue, 10%); // 색상 10% 밝게
  }

  &:active {
    background: darken($blue, 10%); // 색상 10% djenqrp
  }

  & + & {
    margin-left: 1rem;
  }
}
  • & + & : .Button + .Button

4. 버튼 색상 설정하기

  • 기본 색상 외에 다른 색상 설정
  • 색상 참고 사이트 : open-color

button.scss에 색상 추가

$blue: #228be6; // 주석 선언
$gray: #495057;
$pink: #f06595;

.Button {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  color: white;
  font-weight: bold;
  outline: none;
  border-radius: 4px;
  cursor: pointer;

  // 사이즈 관리
  &.large {
    height: 3rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1.25rem;
  }

  &.medium {
    height: 2.25rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1rem;
  }

  &.small {
    height: 1.75rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 0.875rem;
  }

  // 색상 관리
  &.blue {
    background: $blue; // 주석 사용
    &:hover {
      background: lighten($blue, 10%); // 색상 10% 밝게
    }

    &:active {
      background: darken($blue, 10%); // 색상 10% 어둡게
    }
  }

  &.gray {
    background: $gray; // 주석 사용
    &:hover {
      background: lighten($gray, 10%); // 색상 10% 밝게
    }

    &:active {
      background: darken($gray, 10%); // 색상 10% 어둡게
    }
  }

  &.pink {
    background: $pink; // 주석 사용
    &:hover {
      background: lighten($pink, 10%); // 색상 10% 밝게
    }

    &:active {
      background: darken($pink, 10%); // 색상 10% 어둡게
    }
  }

  & + & {
    margin-left: 1rem;
  }
}
  • 반복이 되는 코드 : Sass의 mixin이라는 기능 사용 -> 재사용 가능

button-color라는 mixin 사용

$blue: #228be6; // 주석 선언
$gray: #495057;
$pink: #f06595;

@mixin button-color($color) {
  background: $color;
  &:hover {
    background: lighten($color, 10%);
  }
  &:active {
    background: darken($color, 10%);
  }
}

.Button {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  color: white;
  font-weight: bold;
  outline: none;
  border-radius: 4px;
  cursor: pointer;

  // 사이즈 관리
  &.large {
    height: 3rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1.25rem;
  }

  &.medium {
    height: 2.25rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1rem;
  }

  &.small {
    height: 1.75rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 0.875rem;
  }

  // 색상 관리
  &.blue {
    @include button-color($blue);
  }

  &.gray {
    @include button-color($gray);
  }

  &.pink {
    @include button-color($pink);
  }

  & + & {
    margin-left: 1rem;
  }
}

color 옵션 추가

import './App.scss'
import Button from './components/Button'

function App() {
  return (
    <div className="App">
      <div className="buttons">
        <Button size="large">BUTTON</Button>
        <Button>BUTTON</Button>
        <Button size="small">BUTTON</Button>
      </div>
      <div className="buttons">
        <Button size="large" color="gray">
          BUTTON
        </Button>
        <Button color="gray">BUTTON</Button>
        <Button size="small" color="gray">
          BUTTON
        </Button>
      </div>
      <div className="buttons">
        <Button size="large" color="pink">
          BUTTON
        </Button>
        <Button color="pink">BUTTON</Button>
        <Button size="small" color="pink">
          BUTTON
        </Button>
      </div>
    </div>
  )
}

export default App

4. outline 옵션 만들기

Button.js에 outline 옵션 추가

import classNames from 'classnames'
import React from 'react'
import './Button.scss'

function Button({ children, size, color, outline }) {
  return <button className={classNames('Button', size, color, { outline })}>{children}</button>
}

Button.defaultProps = {
  size: 'medium',
  color: 'blue',
}

export default Button
  • outline 값을 props로 받아와 객체 안에 넣은 후 classNames()에 포함
  • clssNames(..., { outline })
  • outline 값이 true로 적용

Button.scss에 outline 옵션 추가

$blue: #228be6; // 주석 선언
$gray: #495057;
$pink: #f06595;

@mixin button-color($color) {
  background: $color;
  &:hover {
    background: lighten($color, 10%);
  }
  &:active {
    background: darken($color, 10%);
  }
  &.outline {
    color: $color;
    background: none;
    border: 1px solid $color;
    &:hover {
      background: $color;
      color: white;
    }
  }
}

.Button {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  color: white;
  font-weight: bold;
  outline: none;
  border-radius: 4px;
  cursor: pointer;

  // 사이즈 관리
  &.large {
    height: 3rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1.25rem;
  }

  &.medium {
    height: 2.25rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1rem;
  }

  &.small {
    height: 1.75rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 0.875rem;
  }

  // 색상 관리
  &.blue {
    @include button-color($blue);
  }

  &.gray {
    @include button-color($gray);
  }

  &.pink {
    @include button-color($pink);
  }

  & + & {
    margin-left: 1rem;
  }
}

App.js에 버튼 outline 적용

<div className="buttons">
        <Button size="large" color="blue" outline>
          BUTTON
        </Button>
        <Button color="gray" outline>
          BUTTON
        </Button>
        <Button size="small" color="pink" outline>
          BUTTON
        </Button>
      </div>

5. 전체 너비 차지하는 옵션

Button.js에 전체 너비 옵션 추가

import classNames from 'classnames'
import React from 'react'
import './Button.scss'

function Button({ children, size, color, outline, fullWidth }) {
  return <button className={classNames('Button', size, color, { outline, fullWidth })}>{children}</button>
}

Button.defaultProps = {
  size: 'medium',
  color: 'blue',
}

export default Button

Button.css 전체 너비 옵션 추가

$blue: #228be6; // 주석 선언
$gray: #495057;
$pink: #f06595;

@mixin button-color($color) {
  background: $color;
  &:hover {
    background: lighten($color, 10%);
  }
  &:active {
    background: darken($color, 10%);
  }
  &.outline {
    color: $color;
    background: none;
    border: 1px solid $color;
    &:hover {
      background: $color;
      color: white;
    }
  }
}

.Button {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  color: white;
  font-weight: bold;
  outline: none;
  border-radius: 4px;
  cursor: pointer;

  // 사이즈 관리
  &.large {
    height: 3rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1.25rem;
  }

  &.medium {
    height: 2.25rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 1rem;
  }

  &.small {
    height: 1.75rem;
    padding-left: 1rem;
    padding-right: 1rem;
    font-size: 0.875rem;
  }

  // 색상 관리
  &.blue {
    @include button-color($blue);
  }

  &.gray {
    @include button-color($gray);
  }

  &.pink {
    @include button-color($pink);
  }

  & + & {
    margin-left: 1rem;
  }

  &.fullWidth {
    width: 100%;
    justify-content: center;
    & + & {
      margin-left: 0;
      margin-top: 1rem;
    }
  }
}

App.js에 전체 너비 적용

<div className="buttons">
        <Button size="large" fullWidth>
          BUTTON
        </Button>
        <Button size="large" color="gray" fullWidth>
          BUTTON
        </Button>
        <Button size="large" color="pink" fullWidth>
          BUTTON
        </Button>
      </div>

6. ...rest props 전달

Button.js에 이벤트 처리를 넣는다면

  • onClick 넣을 때
import classNames from 'classnames'
import React from 'react'
import './Button.scss'

function Button({ children, size, color, outline, fullWidth, onClick }) {
  return (
    <button className={classNames('Button', size, color, { outline, fullWidth })} onClick={onClick}>
      {children}
    </button>
  )
}

Button.defaultProps = {
  size: 'medium',
  color: 'blue',
}

export default Button
  • onMouseMove 넣을 때
import classNames from 'classnames'
import React from 'react'
import './Button.scss'

function Button({ children, size, color, outline, fullWidth, onClick, onMouseMove }) {
  return (
    <button className={classNames('Button', size, color, { outline, fullWidth })} onClick={onClick} onMouseMove={onMouseMove}>
      {children}
    </button>
  )
}

Button.defaultProps = {
  size: 'medium',
  color: 'blue',
}

export default Button
  • 매번 이렇게 넣기엔 번거로움
    • spread와 rest로 해결

spread와 rest 사용

  • 주로 배열과 객체, 함수의 파라미터, 인자를 다룰 때 사용
  • 컴포넌트에서도 사용

Button 컴포넌트에 사용

import classNames from 'classnames'
import React from 'react'
import './Button.scss'

function Button({ children, size, color, outline, fullWidth, ...rest }) {
  return (
    <button className={classNames('Button', size, color, { outline, fullWidth })} {...rest}>
      {children}
    </button>
  )
}

Button.defaultProps = {
  size: 'medium',
  color: 'blue',
}

export default Button
  • ...rest 사용
    • 우리가 지정한 props 제외한 값들을 rest라는 객체로 모아줌
    • <button> 태그에 {...rest}하면 rest 안에 있는 객체 안에 있는 값들 모두 <button> 태그에 설정

App.js에 ...rest 사용

import './App.scss'
import Button from './components/Button'

function App() {
  return (
    <div className="App">
      <div className="buttons">
        <Button size="large" onClick={() => console.log('클릭!')}>
          BUTTON
        </Button>
        <Button>BUTTON</Button>
        <Button size="small">BUTTON</Button>
      </div>
      <div className="buttons">
        <Button size="large" color="gray">
          BUTTON
        </Button>
        <Button color="gray">BUTTON</Button>
        <Button size="small" color="gray">
          BUTTON
        </Button>
      </div>
      <div className="buttons">
        <Button size="large" color="pink">
          BUTTON
        </Button>
        <Button color="pink">BUTTON</Button>
        <Button size="small" color="pink">
          BUTTON
        </Button>
      </div>
      <div className="buttons">
        <Button size="large" color="blue" outline>
          BUTTON
        </Button>
        <Button color="gray" outline>
          BUTTON
        </Button>
        <Button size="small" color="pink" outline>
          BUTTON
        </Button>
      </div>
      <div className="buttons">
        <Button size="large" fullWidth>
          BUTTON
        </Button>
        <Button size="large" color="gray" fullWidth>
          BUTTON
        </Button>
        <Button size="large" color="pink" fullWidth>
          BUTTON
        </Button>
      </div>
    </div>
  )
}

export default App

참고 자료
https://react.vlpt.us/styling/01-sass.html

좋은 웹페이지 즐겨찾기