styled-jsxの記法をまとめる

フロントエンドエンジニアの根岸です。 css in jsのstyled-jsxを2年ほど使っているのですが、どんな記法があるかちゃんと勉強したことがなかったのでまとめてみました。

基本的な書き方

styled-jsxでスタイルを書くには<style jsx>タグを使います。 下のようにエレメントの中にcssを<style jsx>タグで囲って書くことで、スタイルを適用することができます。 cssは.cssファイルと同様のセレクタを使うことができます。

const Example = () => (
    <div>
        <p>red</p>
        <div className="blue">blue</div>
        <style jsx>{`
          p {
              color: red;
          }
          .blue {
              color: blue;
          }
      `}</style>
    </div>
)

動的スタイル

styled-jsxで書くcssはテンプレートリテラルとして扱われます。 テンプレートリテラルの記法を使うことで動的にスタイルを生成することができます。 下の例ではpropsとしてcolorを受け取って、cssのcolorプロパティに渡しています。

const Example = ({ color }) => (
    <div>
        <p>red</p>
        <style jsx>{`
          p {
              color: ${color};
          }
      `}</style>
    </div>
)

グローバルセレクタ

<style jsx>タグに書いたcssはそのコンポーネントの内部しか適用することができません。 例えば下のようにLayoutコンポーネントを作っても、childrenとして渡されるエレメントにはスタイルが適用されることはありません。

const Layout = ({ children }) => (
    <div className="layout">
      {children}
        <style jsx>{`
      .layout {
          display: flex;
      }
      .layout > * {
          margin: 0 10px;
      }
  `}</style>
    </div>
)

const Example = ({ color }) => (
  <Layout>
        <div>example1</div> {/* divには`.layout > *` のスタイルはあたらない */}
        <div>example2</div>
        <div>example3</div>
    </Layout>
)

このような場合は:globalセレクタを使うことで解決できます。 :globalセレクタを使うとグローバルなスコープでスタイルを適用できるので、<style jsx>タグを定義したコンポーネント外にもスタイルを当てることができます。 ただし:globalセレクタを使うとcssのスコープがグローバルになってしまうので、スコープが広がらないように小セレクタなど使ってスコープを狭めて使うべきでしょう。

const Layout = ({ children }) => (
    <div className="layout">
      {children}
        <style jsx>{`
      .layout {
          display: flex;
      }
      .layout > :global(*) {
          margin: 0 10px;
      }
  `}</style>
    </div>
)

コンポーネントの外側にスタイルを書く

いままでの方法ではコンポーネントの内部に<style jsx>タグを置いてスタイル書いていましたが、コンポーネント外に変数として定義することもできます。 その場合は'styled-jsx/css'からインポートできるcssタグを使います。

cssタグにcssのテンプレートリテラルを渡すとcssを変数として定義できます。 変数化したcssは<style jsx>タグにそのまま渡せばOKです。

import css from 'styled-jsx/css'

const pStyle = css`
  p {
      color:red;
  }
`

const Example = ({ color }) => (
  <div>
        <p>red</p>
        <style jsx>{pStyle}</style>
    </div>
)

また、cssタグはセレクタを定義しなくても使うことができます。 その場合は変数化したcssのclassNameプロパティをcssを適用したいエレメントに渡し、さらにコンポーネント内にcssのstylesプロパティをそのまま置けば適用できます。

import css from 'styled-jsx/css'

const colorRedStyle = css`
  color:red;
`

const Example = ({ color }) => (
  <div>
        <p className={colorRedStyle.className}>red</p>
        {colorRedStyle.styles}
    </div>
)

css.resolve

グローバルスコープのところで<style jsx>タグに書いたcssを外部のコンポーネントに適用したいときは:globalセレクタを使うと書きましたが、css.resolveタグを使うことでも実現できます。 使い方はcssタグを同じで適用したいコンポーネントにclassNameプロパティを渡せばよいです。

import css from 'styled-jsx/css'
import Link from 'next/link'

const linkStyle = css.resolve`
  a { color: red; }
`

const Example = () => (
  <div>
        <Link className={linkStyle.className}>link</Link>
        {linkStyle.styles}
  </div>
)

cssのスコープがグローバルではないので、:globalセレクタよりも安全に使うことができそうです。

まとめ

styled-jsxの記法を一通り見てきました。 2年ほどstyled-jsxを使ってきましたがcssタグの記法は恥ずかしながら知らなかったです。 cssタグを使うとcssが再利用をしやすくなるので積極的に使っていこうと思います。