CSS-in-JS ライブラリ[Emotion]
Emotion は CSS-in-JS ライブラリ(CSS を記述するために設計されたライブラリ)の 1 つで、元々あった styled-components などに影響されて作成されたライブラリです(とくに Gatsby 用とうわけではない)。
Emotion のインストール
- Gatsby プロジェクトディレクトリでインストールを実施
npm install --save gatsby-plugin-emotion @emotion/core @emotion/styled
- gatsby-config.js にプラグインを追加
plugins に
gatsby-plugin-emotion
を追加する
module.exports = {
plugins: [
`gatsby-plugin-emotion`//この部分を追記する
],
}
Emotion で CSS を記述する方法
CSS を記述する方法は CSSprop と StyledComponents の 2 パターンあります。
CSSprop で記述する
Emotion を利用するときにメインとなる方法、要素の css プロパティに CSS の内容を指定します。 css プロパティにはオブジェクト型、文字列型が指定できます。
- まず Emotion を import する [@emotion/core]から jsx と css をインポートし、JSX Pragma [/** @jsx jsx */]を記述します。 (css は文字列型で CSS を指定するときに必要)
/** @jsx jsx */
import { jsx, css } from '@emotion/core'
- 要素に CSS を指定する 要素の css プロパティに CSS の内容を記述したオブジェクトを指定します。
<div
css={{
backgroundColor: 'green',
color: 'white'
}}
>
- サンプルページの作成 CSS の内容を変数で定義するように変更し、文字列型の場合の CSS も追加します。
import React from "react"
/** @jsx jsx */
import { jsx, css } from '@emotion/core'
// オブジェクト型のCSS定義(キャメルケースで記述する)
const obj = {
backgroundColor: 'green',
color: 'white'
}
// 文字列型のCSS定義
const str = `
background-color: blue;
color: white;
`;
export default () => (
<div>
<div css={obj}>
オブジェクト型で指定
</div>
<div css={css`${str}`}>
文字列型で指定
</div>
</div>
)
ブラウザで確認すると、それぞれの CSS が適用されていることが確認できます。
CSS の上書き
CSS を指定したコンポーネントを、別のコンポーネントで利用するときに CSS を上書きできます。 上書きという表現が使われていますが、別のコンポーネントは CSS が後から設定するので優先されるという感じです。
例として以下の 2 つのコンポーネントを作成します。
- NormalP:p タグに backgroundColor、fontSize、color を指定する
- StyleP:NormalP コンポーネントに fontSize、color を指定する
props に className が渡されてくるので、class プロパティの設定をするためコンポーネントに展開します。
import React from "react"
/** @jsx jsx */
import { jsx } from '@emotion/core'
// NormalPコンポーネントの定義
const NormalP = props => (
<p
css={{
backgroundColor:'moccasin',
fontSize: 12,
color: 'maroon',
}}
{...props}//propsにclassNameが送られてくる、htmlのclassプロパティ用に展開
/>
)
// StylePコンポーネントの定義
const StyleP = props => (
<NormalP
css={{
fontSize: 30,
color: 'springgreen'
}}
{...props}
/>
)
export default () => (
<div>
<NormalP>
NormalPの内容
</NormalP>
<StyleP>
StylePの内容
</StyleP>
</div>
)
ブラウザで確認すると、StyleP コンポーネントの CSS には StyleP 定義時に指定した fontSize、color で上書きされていますが、backgroundColor は NormalP の CSS がそのまま使われています。
仕組みとしては以下の様に処理を行っているようでした。
- まず StyleP コンポーネントの処理に入り、props に className が設定され NormalP コンポーネントを呼び出す。
- NormalP コンポーネントが処理され p タグを作成する、この p タグに NormalP の CSS を設定し、props を展開するので className が class プロパティとして出力される。
- StyleP が p タグに CSS を設定する。
- HTML として出力。
HTML ソースを確認すると、StyleP コンポーネントが出力した p タグに NormalP の 「backgroundColor:‘moccasin’」「fontSize: 12」が取り消されているのが確認できます。
props をすべて展開したくない場合は、className プロパティと props.children を自分で設定しても同じ結果になります。
// NormalPコンポーネントの定義
const NormalP = props => (
<p
css={{
backgroundColor:'moccasin',
fontSize: 12,
color: 'maroon',
}}
className={props.className}{/* classNameを自分で設定*/}
>
{props.children}{/* 子要素の設定*/}
</p>
)
StyledComponents で記述する
StyledComponents の記述は他の CSSinJS の styled-components や glamorous に似ており、 コンポーネント定義時に CSS も定義する方法となります
- StyledComponents で記述するには styled のインポートをインポートします。
import styled from '@emotion/styled'
- コンポーネント作成時に styled を使用し、要素と CSS を指定します。
// DIV_TESTコンポーネントはdivタグでcolor: red
const DIV_TEST = styled.div`
color: red;
`
- サンプルページとして CSSprop の例で作成した NormalP コンポーネントを作成してみます。
import React from "react"
import { css } from "@emotion/core"
import styled from "@emotion/styled"
// NormalPコンポーネントの定義
const NormalP = styled.p`
background-color:moccasin;
font-size: 12px;
color: maroon;
`
export default () => (
<div>
<NormalP>
NormalPの内容
</NormalP>
</div>
)
同じ内容が表示されます。
CSS の上書き
StyledComponents で別のコンポーネントの CSS を上書きする場合には、styled(コンポーネント名)と記述します。 CSSprop と同様に以下の 2 つのコンポーネントを作成します。
- NormalP:p タグに backgroundColor、fontSize、color を指定する
- StyleP:NormalP コンポーネントに fontSize、color を指定する
import React from "react"
import { css } from "@emotion/core"
import styled from "@emotion/styled"
// NormalPコンポーネントの定義
const NormalP = styled.p`
background-color:moccasin;
font-size: 12px;
color: maroon;
`
// StylePコンポーネントの定義(NormalPのCSSの内容を取得する)
const StyleP = styled(NormalP)`
font-size: 30px;
color: springgreen;
`
export default () => (
<div>
<NormalP>
NormalPの内容
</NormalP>
<StyleP>
StylePの内容
</StyleP>
</div>
)
ブラウザの表示結果や、HTML ソースが同じように出力されていることが確認できます。