LoginSignup
57
45

More than 5 years have passed since last update.

styled-components

Posted at

styled-componentsについて

CSS in JSを導入する前の調査メモ。


styled-componentsはcssをJSオブジェクト形式で記述するタイプではなく、CSSのsyntaxをほぼそのまま使って書くことができるタイプのcss in js
css in jsでは、cssを定義してコンポーネントに受けわたすのを記述するのが冗長で煩雑になりがちだけど、styled-componentsではよりシンプルで直感的な記述ができる。

// glamor(https://github.com/threepointone/glamor)
const styles = glamor.css({
  fontSize: '1em',
})

function Button() {
  return <button className={ `$ { styles }` }>button</button>
}
// styled-components
const Button = styled.button`
  font-size: 1em;
`

classNameを受け取れるコンポーネント全てに対してstyledが使える。
styled('div')でもstyled.tagnameでもどちらでも問題ない。styled.tagnamestyled()のエイリアス。
styled-componentsが生成するのはclassセレクタの本物のstylesheetで、ComponentにclassNamepropsで渡される。

styled componentsが生成するclassセレクタは、ユニークな文字列でできている。

/* sc-component-id: sc-bdVaJa */
.sc-bdVaJa {}
.cFApLu {font-size: 1rem;}

sc-で始まるルールが空のクラスが何なのか、今のところよくわからない。

tagged template literal

styled-componentsでは何故ほぼCSSままのsyntaxで記述できるのか。それはタグ付きテンプレートリテラルによって実現されてる。

const Headline = styled.h1`
  font-size: 2rem;
`

タグ付きテンプレートリテラルとは何なのか。ここで詳しく解説してる。
https://mxstbr.blog/2016/11/styled-components-magic-explained/

タグ付けされたTemplate literalでは、関数を使ってTemplate literalのアウトプットを調整できます。
テンプレート文字列 - JavaScript | MDN

タグ付きテンプレートリテラルはes2015から使える。

const log = (...args) => console.log(...args)

上記のような関数を定義してconsoleargumentsをすべて出力するようにして、実行の仕方や渡す値によって出力されるものがどう変わるか見てみる。

()にテンプレートリテラルを渡した場合

log(`
  font-size: 12px;
`)
// -> font-size: 12px;

log()で実行すると、argumentsは文字列ままで返ってくる。

タグ付きテンプレートリテラルの場合

log`
  font-size: 12px;
`
// -> ['font-size: 12px;', raw: Array(1)]

テンプレートの中身が文字列の場合、argumentsは配列で返ってくる。

タグ付きテンプレートリテラルにプレースホルダで変数を受け取る場合

log`
  font-size: ${ fs12 }
`
// -> ['↵  font-size: ', '↵', raw: Array(2)] '12px'

テンプレートでプレースホルダから変数を渡すと、arguments[1]にその値が入る。

タグ付きテンプレートリテラルにプレースホルダで関数を受け取る場合

log`
  font-size: ${ (props) => { props ? 12 : 14 } }px;
`
// -> ['↵  font-size: ', '↵', raw: Array(2)] (props) => { props ? 12 : 14 }

プレースホルダに関数を渡すと、arguments[1]には関数がそのまま入っている。

styled-componentsはこのタグ付きテンプレートリテラルの特性を利用して、以下のような記述が成り立つようになっているらしい。

const TextField = styled.input`
  background-color: ${ props => props.error ? '#ff6b6b' : '#efefef' };
`

API

styled

タグ付きテンプレートリテラルを受け取ってstyled componentを返す関数を生成する。

const Title = styled.h1`
  font-size: 2em;
  font-weight: bold;
`

extend

<Button />コンポーネントを拡張した<LoginButton />のようなコンポーネントを作成したい場合に使える。

const Button = styled.button`
  padding: 1em;
  background-color: #fff;
  color: #1c7cd6;
  font-size: 14px;
  border: 1px solid #1c7cd6;
`

const LoginButton = Button.extend`
  background-color: #4c6ef5;
  color: #fff;
  font-size: 18px;
`

withComponent

withComponentは、styled-componentsでstyleがあてられたコンポーネントをそのコンポーネントのタグとは異なるタグで拡張したい場合に利用する。

const Link = Button.withComponent('a')

attrs

attrsは、ラッパーコンポーネントを作ることなくコンポーネントにpropsを渡したい場合に利用する。

const PasswordInput = styled('input').attrs({
  type: 'password',
})`
  padding: 1em;
  color: ${ black };
`

css

タグ付きテンプレートリテラルのinterpolations(正確な意味はよく分からないけど、手を加えて値を返すような意味合いっぽい)からCSSを生成するヘルパー。
実際の使い方としては、以下のようにタグ付きテンプレートの中で、テンプレートリテラルをネストしてCSSを返したい場合に使う。

const Button = styled.button`
  ${ props => props.disabled && css`
    opacity: 0.7;
    pointer-events: none;
  `}
`

ThemeProvider

テーマ用のヘルパー。themepropsを通して<ThemeProvider />配下のstyled componentにテーマのスタイルをあてる。

const styled, { ThemeProvider } from 'styled-components'

const Btn = styled.button`
  background-color: ${ props => props.theme.backgroundColor };
  color: ${ props => props.theme.color };
`

<ThemeProvider theme={
  {
    backgroundColor: '#7950f2',
    color: '#fff',
  }
}>
  <Btn>button</Btn>
</ThemeProvider>

withTheme

withThemeは、styled components以外に対してテーマをあてたい場合に利用するHigher Order Component

import { withTheme } from 'styled-components'

class Wrapper extends React.Component {
  render() {
    return (
      <Button theme={ this.props.theme }>button</Button>
    )
  }
}

export default withTheme(Wrapper)

Animation

keyframesヘルパーを利用することでユニークな名前の@keyframesを作ることができる。

import { keyframes } from 'styled-components';

const rotation = keyframes`
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
`

/* sc-component-id: sc-keyframes-iVXCSc */
@-webkit-keyframes iVXCSc {from {-webkit-transform: rotate(0deg);-ms-transform: rotate(0deg);transform: rotate(0deg);}to {-webkit-transform: rotate(360deg);-ms-transform: rotate(360deg);transform: rotate(360deg);}}@keyframes iVXCSc {from {-webkit-transform: rotate(0deg);-ms-transform: rotate(0deg);transform: rotate(0deg);}to {-webkit-transform: rotate(360deg);-ms-transform: rotate(360deg);transform: rotate(360deg);}}

injectGlobal

styled-componentsは自動的にユニークな文字列でセレクタ名を生成するけど、injectGlobalヘルパーを使うと記述したセレクタ名ままで出力できる。

import { injectGlobal } from 'styled-components'

injectGlobal`
  body {
    background-color: #fff;
  }
  .global {
    color: #ccc;
  }

/* sc-component-id: sc-global-1896152519 */
 body {background-color: #fff;} .global {color: #ccc;}

vendor prefix

vendor prefixは自動付与されるので、autoprefixerは必要ない。

const Wrapper = styled.div`
  display: flex;
`

/* sc-component-id: sc-htoDjs */
.sc-htoDjs {}
.iOTXGv {display: -webkit-box;display: -webkit-flex;display: -ms-flexbox;display: flex;}

Lint

css in jsだけど、ほぼCSSのsyntaxということもあってか、styled-components用のprocessorを利用すればstylelintが使える。

$ yarn add --dev stylelint-processor-styled-components stylelint stylelint-config-standard

でnpmをインストールしたら.stylelintrcを以下のように用意するだけ。

{
  "processors": ["stylelint-processor-styled-components"],
  "extends": "stylelint-config-standard",
  "syntax": "scss"
}

syntaxをscssにするのは、ネストやタグ付きテンプレートリテラルの関数をサポートするため。


Links

57
45
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
57
45