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.tagname
はstyled()
のエイリアス。
styled-componentsが生成するのはclassセレクタの本物のstylesheetで、ComponentにclassName
propsで渡される。
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)
上記のような関数を定義してconsole
にarguments
をすべて出力するようにして、実行の仕方や渡す値によって出力されるものがどう変わるか見てみる。
()にテンプレートリテラルを渡した場合
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
テーマ用のヘルパー。theme
propsを通して<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
- 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