ちょっとした技術メモを忘れないうちに書いていく

GatsbyJS CSSを指定する(CSS-in-JS Emotion)

2019-06-05

Emotionを使って、CSSpropでスタイルを指定する方法とStyledComponentsでスタイルを指定する方法


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 プロパティにはオブジェクト型、文字列型が指定できます。

  1. まず Emotion を import する [@emotion/core]から jsx と css をインポートし、JSX Pragma [/** @jsx jsx */]を記述します。 (css は文字列型で CSS を指定するときに必要)
/** @jsx jsx */
import { jsx, css  } from '@emotion/core'
  1. 要素に CSS を指定する 要素の css プロパティに CSS の内容を記述したオブジェクトを指定します。
<div
css={{
    backgroundColor: 'green',
    color: 'white'
}}
>
  1. サンプルページの作成 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 が適用されていることが確認できます。 cssprop_1.png

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 がそのまま使われています。 cssprop_2.png

仕組みとしては以下の様に処理を行っているようでした。

  1. まず StyleP コンポーネントの処理に入り、props に className が設定され NormalP コンポーネントを呼び出す。
  2. NormalP コンポーネントが処理され p タグを作成する、この p タグに NormalP の CSS を設定し、props を展開するので className が class プロパティとして出力される。
  3. StyleP が p タグに CSS を設定する。
  4. HTML として出力。

cssprop_5.png

HTML ソースを確認すると、StyleP コンポーネントが出力した p タグに NormalP の 「backgroundColor:‘moccasin’」「fontSize: 12」が取り消されているのが確認できます。 cssprop_4.png

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 も定義する方法となります

  1. StyledComponents で記述するには styled のインポートをインポートします。
import styled from '@emotion/styled'
  1. コンポーネント作成時に styled を使用し、要素と CSS を指定します。
// DIV_TESTコンポーネントはdivタグでcolor: red
const DIV_TEST = styled.div`
    color: red;
`
  1. サンプルページとして 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>
)

同じ内容が表示されます。

StyledComponents_1.png

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 ソースが同じように出力されていることが確認できます。 StyledComponents_2.png

参考

Emotion 公式サイト