본문 바로가기

FrontEnd

[Epic React][실전] 스타일 추가하기 with Emotion👩‍🎤

반응형

bookshelf/INSTRUCTIONS.md at exercises/02-styles · kentcdodds/bookshelf (github.com)

 

GitHub - kentcdodds/bookshelf: Build a ReactJS App workshop

Build a ReactJS App workshop. Contribute to kentcdodds/bookshelf development by creating an account on GitHub.

github.com

Background

React 앱의 스타일을 지정하는 방법에는 여러 가지가 있으며 각 접근 방식에는 고유한 트레이드오프가 있다.
하지만 궁극적으로 모두 스타일시트와 인라인 스타일이다.
웹팩을 사용하기 때문에 CSS 파일을 애플리케이션으로 직접 가져올 수 있고
CSS의 cascading(계단식) 특성을 일부 상황에서 유리하게 활용할 수 있습니다.
대규모 애플리케이션 개발에는 https://emotion.sh/👩‍🎤 라이브러리 사용이 좋다.
이 라이브러리는 JavaScript에서 CSS를 작성할 수 있는 "CSS-in-JS"라는 접근 방식을 사용한다.

CSS-in-JS 더 알아보기

 

A Unified Styling Language

In the past few years we’ve seen the rise of CSS-in-JS, emerging primarily from within the React community — but why?

medium.com

CSS-in-JS를 사용하면 다음과 같은 것이 가능하다.

  • "스타일을 전달하는" 컴포넌트 만들기.
  • 컴포넌트에 스타일 적용하기.

Making a styled component with emotion

가장 기본적인 방법

import styled from '@emotion/styled'

const Button = styled.button`
  color: turquoise;
`

오브젝트 스타일로 만들기 (kent는 이 방법을 더 좋아한다 함.)

왜 이 방법이 더 좋을까? 함수 적용부터 보면 와닿을 것이다.

(객체 내(코드)에서 함수 적용하기 vs string interpolation 무한 중복하기)

const Button = styled.button({
  color: 'turquoise',
})

props를 이용하여 함수 적용도 가능

const Box = styled.div(props => {
  return {
    height: props.variant === 'tall' ? 150 : 80,
  }
})

// or with the string form:

const Box = styled.div`
  height: ${props => (props.variant === 'tall' ? '150px' : '80px')};
`

// then you can do:
// <Box >

Using emotion's css prop

styled-component는 코드 재사용시 정말 유리함.

일회용으로 쓸 경우는 불편함. Wrapper나 Container를 막 만들게 됨.

emotion을 사용하면 해당 컴포넌트에 css를 주입할 수 있음.

컴포넌트를 만들 때에는 html만 고려하고, css는 주입하라!

기본적인 css prop 활용법은 다음과 같음.

 

1. 파일 상단에 해당 라인 추가

/** @jsx jsx */
import {jsx} from '@emotion/core'
import * as React from 'react'

React.CreateElement를 위와 같이 대체하게 됨.
이것도 가능함 ㅋㅋ

2. 이모션의 css prop을 아래와 같이 사용 가능

// 오브젝트 스타일

function SomeComponent() {
  return (
    <div
      css={{
        backgroundColor: 'hotpink',
        '&:hover': {
          color: 'lightgreen',
        },
      }}
    >
      This has a hotpink background.
    </div>
  )
}

// or with string syntax:

function SomeOtherComponent() {
  const color = 'darkgreen'

  return (
    <div
      css={css`
        background-color: hotpink;
        &:hover {
          color: ${color};
        }
      `}
    >
      This has a hotpink background.
    </div>
  )
}

이모션은 커스텀 jsx 함수로 위의 결과를 아래와 같은 컴포넌트로 컴파일함.

function SomeComponent() {
  return <div className="css-bp9m3j">This has a hotpink background.</div>
}

실습

스타일드 컴포넌트 만들어보기

src/components/lib.final.js

import styled from '@emotion/styled'
import {Dialog as ReachDialog} from '@reach/dialog'

const buttonVariants = {
  primary: {
    background: '#3f51b5',
    color: 'white',
  },
  secondary: {
    background: '#f1f2f7',
    color: '#434449',
  },
}

// 첫번째 style객체와, 두번째 함수 호출 리턴 style 객체를 합쳐준다.
const Button = styled.button(
  {
    padding: '10px 15px',
    border: '0',
    lineHeight: '1',
    borderRadius: '3px',
  },
  ({variant = 'primary'}) => buttonVariants[variant], 
)

const Input = styled.input({
  borderRadius: '3px',
  border: '1px solid #f1f1f4',
  background: '#f1f2f7',
  padding: '8px 12px',
})

const CircleButton = styled.button({
  borderRadius: '30px',
  padding: '0',
  width: '40px',
  height: '40px',
  lineHeight: '1',
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'center',
  background: 'white',
  color: '#434449',
  border: `1px solid #f1f1f4`,
  cursor: 'pointer',
})

const Dialog = styled(ReachDialog)({
  maxWidth: '450px',
  borderRadius: '3px',
  paddingBottom: '3.5em',
  boxShadow: '0 10px 30px -5px rgba(0, 0, 0, 0.2)',
  margin: '20vh auto',
  '@media (max-width: 991px)': {
    width: '100%',
    margin: '10vh auto',
  },
})

const FormGroup = styled.div({
  display: 'flex',
  flexDirection: 'column',
})

export {Button, Input, CircleButton, Dialog, FormGroup}

 

 

Emotion Macro로 React Devtools 강화

매크로는 바벨 플러그인이 설정 안해도 자동으로 지원해주는 코드 변환 기능.

데브툴에서 emotion styled component를 사용하면 기본적으로 아래와 같이, html elemet로 표기

 

FormGroup 컴포넌트가 있어야 할 위치

import를 아래와 같이 바꿔준다.

import styled from '@emotion/styled/macro'

devtool에서 컴포넌트로 잘보임
css도 바꿔줌

📜 Learn more about macros:

 

GitHub - kentcdodds/babel-plugin-macros: 🎣 Allows you to build simple compile-time libraries

🎣 Allows you to build simple compile-time libraries - GitHub - kentcdodds/babel-plugin-macros: 🎣 Allows you to build simple compile-time libraries

github.com

컬러와 미디어 쿼리 적용하기

아래와 같은 방법으로 테마를 한번에 바꾸는 방법도 있음.

UI 표준이 정해져 있다면 해당 방식을 사용하는 것도 좋아 보임.

실습에서는 개별적으로 스타일을 바꿔본다.

https://emotion.sh/docs/theming

 

Emotion - Theming

Theming is included in the @emotion/react package. Add ThemeProvider to the top level of your app and access the theme with props.theme in a styled component or provide a function that accepts the theme as the css prop. Table of Contents Examples css prop

emotion.sh

src/styles/colors.js

export const base = 'white'
export const text = '#434449'
export const gray = '#f1f2f7'
export const gray10 = '#f1f1f4'
export const gray20 = '#e4e5e9'
export const gray80 = '#6f7077'
export const indigo = '#3f51b5'
export const indigoDarken10 = '#364495'
export const indigoLighten80 = '#b7c1f8'
export const yellow = '#ffc107'
export const green = '#4caf50'
export const danger = '#ef5350'
export const orange = 'orange'

 

src/styles/media-queries.js

export const large = '@media (min-width: 1200px)'
export const medium = '@media (min-width: 992px) and (max-width: 1199px)'
export const small = '@media (max-width: 991px)'

가져다 쓰기

import * as colors from 'styles/colors'
import * as mq from 'styles/media-queries'
import {Dialog as ReachDialog} from '@reach/dialog'


// 인풋 - 색상
const Input = styled.input({
  borderRadius: '3px',
  border: `1px solid ${colors.gray10}`,
  background: colors.gray,
  padding: '8px 12px',
})

// 모달 대화창 - 미디어 쿼리
const Dialog = styled(ReachDialog)({
  maxWidth: '450px',
  borderRadius: '3px',
  paddingBottom: '3.5em',
  boxShadow: '0 10px 30px -5px rgba(0, 0, 0, 0.2)',
  margin: '20vh auto',
  [mq.small]: {
    width: '100%',
    margin: '10vh auto',
  },
})

스피너 만들기 (keyframe, animation)

aria-label이 있어야 화면이 보이지 않는 사람들도 웹을 이용할 수 있음.

import styled from '@emotion/styled/macro'
import {keyframes} from '@emotion/core'
import {FaSpinner} from 'react-icons/fa'

const spin = keyframes({
  '0%': {transform: 'rotate(0deg)'},
  '100%': {transform: 'rotate(360deg)'},
})

const Spinner = styled(FaSpinner)({
  animation: `${spin} 1s linear infinite`,
})
Spinner.defaultProps = {
  'aria-label': 'loading',
}

Keyframe Animation Syntax | CSS-Tricks - CSS-Tricks

 

Keyframe Animation Syntax | CSS-Tricks

You can use any number of "stops" in the @keyframe animation, and it's one of the main strengths of keyframe animations. While CSS transition is only from one

css-tricks.com

emotion 더 많이 알아보기

https://emotion.sh/docs/styled

 

Emotion - Styled Components

styled is a way to create React components that have styles attached to them. It’s available from @emotion/styled. styled was heavily inspired by styled-components and glamorous Styling elements and components styled is very similar to css except you cal

emotion.sh

 jsx pragma 안쓰는 방법 참고 (아래 요소) - CRA는 안됨. craco같은거 찾아봐야할듯.

/** @jsx jsx */

📜 https://emotion.sh/docs/css-prop

 

Emotion - The css Prop

The primary way to style elements with emotion is the css prop. It provides a concise and flexible API to style your components. Get Started There are 2 ways to get started with the css prop. Both methods result in the same compiled code. After adding the

emotion.sh

개인적인 소견

emotion.js로 컴포넌트의 재사용성을 높였으나,

css 작성은 역시 너무 저수준의 작업이다.

반응형