styled-components

# styled-componentsについて

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

---

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

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

function Button() {
  return <button className={ `$ { styles }` }>button</button>
}
```

```js
// styled-components
const Button = styled.button`
  font-size: 1em;
`
```

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

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

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

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

## tagged template literal

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

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

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

> タグ付けされたTemplate literalでは、関数を使ってTemplate literalのアウトプットを調整できます。
[テンプレート文字列 - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/template_strings#%E3%82%BF%E3%82%B0%E4%BB%98%E3%81%91%E3%81%95%E3%82%8C%E3%81%9FTemplate_literal)

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


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

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

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

```js
log(`
  font-size: 12px;
`)
// -> font-size: 12px;
```
`log()`で実行すると、argumentsは文字列ままで返ってくる。

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

```js
log`
  font-size: 12px;
`
// -> ['font-size: 12px;', raw: Array(1)]
```
テンプレートの中身が文字列の場合、argumentsは配列で返ってくる。

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

```js
log`
  font-size: ${ fs12 }
`
// -> ['↵  font-size: ', '↵', raw: Array(2)] '12px'
```
テンプレートでプレースホルダから変数を渡すと、`arguments[1]`にその値が入る。

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

```js
log`
  font-size: ${ (props) => { props ? 12 : 14 } }px;
`
// -> ['↵  font-size: ', '↵', raw: Array(2)] (props) => { props ? 12 : 14 }
```
プレースホルダに関数を渡すと、`arguments[1]`には関数がそのまま入っている。

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

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


## API

### `styled`

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

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

### `extend`

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

```js
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があてられたコンポーネントをそのコンポーネントのタグとは異なるタグで拡張したい場合に利用する。

```js
const Link = Button.withComponent('a')
```

### `attrs`

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

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

### `css`

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

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

### `ThemeProvider`

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

```js
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](https://facebook.github.io/react/docs/higher-order-components.html)。

```js
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`を作ることができる。

```js
import { keyframes } from 'styled-components';

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

```css
/* 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`ヘルパーを使うと記述したセレクタ名ままで出力できる。

```js
import { injectGlobal } from 'styled-components'

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

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

## vendor prefix

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

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

```css
/* 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](https://stylelint.io/)が使える。

https://github.com/styled-components/stylelint-processor-styled-components

```bash
$ yarn add --dev stylelint-processor-styled-components stylelint stylelint-config-standard
```
でnpmをインストールしたら`.stylelintrc`を以下のように用意するだけ。

```json
{
  "processors": ["stylelint-processor-styled-components"],
  "extends": "stylelint-config-standard",
  "syntax": "scss"
}
```
syntaxを`scss`にするのは、ネストやタグ付きテンプレートリテラルの関数をサポートするため。

---

## Links

- https://www.styled-components.com/
- https://medium.freecodecamp.com/a-5-minute-intro-to-styled-components-41f40eb7cd55
- https://mxstbr.blog/2016/11/styled-components-magic-explained/
- https://github.com/romellogood/awesome-styled-components
- https://medium.com/styled-components/announcing-v2-f01ef3766ac2
- http://yoshiko.hatenablog.jp/entry/css-in-js
- https://hackernoon.com/introducing-glamorous-fb3c9f4ed20e