Styled-tools로 좀더 편리하게 Styled-components 사용하기

Styled-tools는 CSS in JS 라이브러리(Styled-components, emotion, JSS, etc...)로 컴포넌트 스타일링을 할 때 같이 사용할 수 있는 추상화된 유용한 함수들입니다.

Styled-tools를 사용하신다면 좀 더 가독성이 높고 편리하게 스타일링을 하실 수 있으실 겁니다.

이 글에선 많은 CSS in JS 라이브러리들 중 가장 대중적인 Styled-components와 함께 사용해보도록 하겠습니다.

⚠️ 이 글은 Styled-components의 기본적인 문법에 대해 알고 있다고 가정하고 작성됐습니다.

설치

먼저 npm이나 yarn을 사용해서 styled-tools와 CSS in JS 라이브러리를 설치해줍니다.

npm i styled-tools styled-components
# 또는 yarn add styled-tools styled-components

prop

prop(path: string, defaultValue?: any): PropsFn

첫 번째 인자로 path를 받고 두 번째 인자로 기본값을 받습니다.

해당 함수는 props[path]가 있으면 해당 값을 반환하고 없는경우 두 번째 인자로 입력받은 기본값을 반환합니다.

import styled from 'styled-components'
import { prop } from 'styled-tools'

const Button = styled.button`
  color: ${prop('color', 'red')};
`

// 해당 버튼과 동일
const Button2 = styled.button`
  color: ${({ color }) => color || 'red'};
`

theme

theme(path: string, defaultValue?: any): PropsFn

해당 함수는 prop와 동일하게 첫 번째 인자로 path를 받고 두 번째 인자로 기본값을 받습니다.

prop와의 차이점은 해당 함수는 props[path]를 조회하는 게 아니라 props.theme[path]를 조회합니다.

import styled from 'styled-components'
import { theme } from 'styled-tools'

const Button = styled.button`
  color: ${theme('button.color', 'red')};
`

// 해당 버튼과 동일
const Button2 = styled.button`
  color: ${({ theme }) => theme.button.color || 'red'};
`

palette

palette(keyOrTone?: string | number, toneOrDefaultValue?: any, defaultValue?: any): PropsFn

해당 함수는 첫 번째 인자로 keytone을 받고 두 번째 인자로 tone또는 기본값을 받습니다 마지막 인자로 기본값을 받습니다.

첫 번째 인자로 index를 넘겨줄경우 props.theme.palette[props.palette][tone]을 반환하며,

key를 넘겨줄경우 props.theme.palette[key][tone]을 반환합니다.

import styled, { ThemeProvider } from 'styled-components'
import { palette } from 'styled-tools'

const theme = {
  palette: {
    primary: ['#1976d2', '#2196f3', '#71bcf7', '#c2e2fb'],
    secondary: ['#c2185b', '#e91e63', '#f06292', '#f8bbd0']
  }
}

const Button = styled.button`
  color: ${palette(1)};                   // props.theme.palette[props.palette][1]
  color: ${palette('primary', 1)};        // props.theme.palette.primary[1]
  color: ${palette('primary')};           // props.theme.palette.primary[props.tone || 0]
  color: ${palette('primary', -1)};       // props.theme.palette.primary[3] 뒤에서 1번째 index
  color: ${palette('primary', 10)};       // props.theme.palette.primary[3]
  color: ${palette('primary', -10)};      // props.theme.palette.primary[0]
  color: ${palette('primary', 0, 'red')}; // props.theme.palette.primary[0] || red
`

;<ThemeProvider theme={theme}>
  <Button palette="secondary" />
</ThemeProvider>

ifProp

ifProp(test: string | string[] | object, pass: any = "", fail: any = ""): PropsFn

첫 번째 인자로 test할 값을 받고 두 번째 인자로 test할 값이 props에 존재할 때 반환할 값을 받습니다 마지막 인자로는 props에 존재하지 않을 때 반환할 값을 받습니다.

첫 번째 인자로 배열과 객체를 넘겨줄 수 있는데 배열을 넘겨줄 경우, 배열 안의 모든 값이 다 props에 존재하면 pass 값이 반환되고 존재하지 않으면 fail 값이 반환됩니다.

객체를 넘겨줄 경우, 객체의 property 값이 value 값인 데이터가 props에 존재하면 pass 값이 반환되고 존재하지 않으면 fail 값이 반환됩니다.

import styled from 'styled-components'
import { ifProp, palette } from 'styled-tools'

const Button = styled.button`
  background-color: ${ifProp('transparent', 'transparent', palette(0))};
  color: ${ifProp(['transparent', 'accent'], palette('secondary'))};
  font-size: ${ifProp(
    { size: 'large' },
    '20px',
    ifProp({ size: 'medium' }, '16px', '12px')
  )};
`

// 해당 버튼과 동일
const Button2 = styled.button`
  background-color: ${({ transparent, theme, palette }) =>
    transparent ? 'transparent' : theme.palette[palette][0]};
  color: ${({ transparent, accent, theme, palette }) =>
    transparent && accent ? theme.palette.secondary : ''};
  font-size: ${({ size }) =>
    size === 'large' ? '20px' : size === 'medium' ? '16px' : '12px'};
`

ifNotProp

ifNotProp(test: string | string[] | object, pass: any = "", fail: any = ""): PropsFn

ifProp와 동일하지만 test할 값이 props에 존재하지 않을 때 pass값을 반환하고 존재할 때 fail값을 반환합니다.

import styled from 'styled-components'
import { ifNotProp, palette } from 'styled-tools'

const Button = styled.button`
  background-color: ${ifNotProp('transparent', 'transparent', palette(0))};
  color: ${ifNotProp(['transparent', 'accent'], palette('secondary'))};
  font-size: ${ifNotProp(
    { size: 'large' },
    '20px',
    ifNotProp({ size: 'medium' }, '16px', '12px')
  )};
`

// 해당 버튼과 동일
const Button2 = styled.button`
  background-color: ${({ transparent, theme, palette }) =>
    !transparent ? 'transparent' : theme.palette[palette][0]};
  color: ${({ transparent, accent, theme, palette }) =>
    !(transparent && accent) ? theme.palette.secondary : ''};
  font-size: ${({ size }) =>
    size !== 'large' ? '20px' : size !== 'medium' ? '16px' : '12px'};
`

withProp

withProp(path: string | string[] | object, fn: function): PropsFn

첫 번째 인자로 path를 받고 두 번째 인자로 함수를 받습니다.

props[path]값을 조회한 뒤 해당 값을 두 번째 인자로 받은 함수의 인자로 넣어줘서 함수의 리턴값을 반환합니다.

import styled from 'styled-components'
import { withProp, prop } from "styled-tools";

const Button = styled.button`
  border: ${withProp(prop("theme.primaryColor", "blue"), color => `1px solid ${color}`)};
  font-size: ${withProp("theme.size", size => `${size + 1}px`)};
  background: ${withProp(["foo", "bar"], (foo, bar) => `${foo}${bar}`)};
`;

// 해당 버튼과 동일
const Button2 = styled.button`
  border: ${({theme}) => `1px solid ${theme.primaryColor || 'blue'}`};
  font-size: ${({theme}) => `${theme.size}px`};
  background: ${({foo, bar}) => `${foo}${bar}`};
`

Reference

comment

Comments