Next.jsとCSS

ReactでCSSを書くにはたくさんの方法があると思います。
その中でもNext.jsでそれらを使用するにはどうしたらよいかを一覧で書いてみました。
それぞれのフレームワークの記法にはあまり触れずに、Next.jsをプロダクションレディーにするための設定とGlobalなCSSの書き方に触れます。

# CSS not in JS

## Sass with CSS modules

Next.jsのconfigに以下を追加

```next.config.js
const withSass = require('@zeit/next-sass')
module.exports = withSass({
  cssModules: true
})
```

Sassファイルのstyles.scssを作成

```scss:styles.scss
$font-size: 50px;
.example {
  font-size: $font-size;
}
```

コンポーネント内で`className`としてオブジェクトを引き出します

```react:pages/index.js

import css from "../styles.scss"

export default () => <div className={css.example}>Hello World!</div>
```
これでスコープドなCSSが書けます。

### Globalなスタイル
Next.jsではクライアントの全ページで使用されるのは`pages/_app.js`なので、こちらでimportすればOKです。`pages/_document.js`でのimportは効きません。

```react:pages/_app.js
import "../styles/global.scss";
```


また、直書きで良いのであれば、 `pages/_document.js`にテンプレートリテラルで書く方法もあります。

```react:/pages/_document.js
import Document, { Head, Main, NextScript } from 'next/document'

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx)
    return { ...initialProps }
  }

  render() {
    return (
      <html>
        <Head>
          {/* このようにテンプレートリテラルで直書きする */}
          <style>{`body { margin: 0 } /* custom! */`}</style>
        </Head>
        <body className="custom_class">
          <Main />
          <NextScript />
        </body>
      </html>
    )
  }
}
```


https://github.com/zeit/next.js/wiki/Global-styles-and-layouts

# CSS in JS
## styled-jsx
Next.jsを作ったZeitがstyled-jsxもOSSとして作っています。
なのでNext.jsではデフォルトでstyled-jsxがデフォルトでロードされていて、no configで実装が開始できます。

```react:pages/index.js
export default () => (
  <div>
    Welcome to next.js!
    <style jsx>{`
      div {
        background: red;
      }
    `}</style>
  </div>
);
```
[詳しい記法などはこちらを参照して下さい。](https://github.com/zeit/styled-jsx)

### Globalなスタイル

styled-jsx記法ではglobalというパラメータをつけると、書いたクラス名やタグがグローバルなものとして出力されます。これも同じく`_app.js`内で記載するとよいでしょう。

```react:pages/_app.js
import React from "react";
import App, { Container } from "next/app";

export default class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props;

    return (
      <Container>
        <Component {...pageProps} />
        <style jsx global>{`
          body {
            background-color: green;
          }
        `}</style>
      </Container>
    );
  }
}

```
## styled-components


```shell:shell
npm i --save styled-components
npm i -D babel-plugin-styled-components
```

```json:.babelrc
{
  "presets": [
    "next/babel"
  ],
  "plugins": [
    "styled-components"
  ]
}
```

styled-componentsをNext.jsで使用するためには、実はこれだけでは足りません。サーバーサイドレンダリングされる際にstyled-compoentnsが実行されないため、クライアントでReactが実行されてからスタイルが当たるようになっています。ですから、`<head>`タグにスタイルを入れるために`ServerStyleSheet`を`_document.js`で実行しましょう。[こちらに例が載っています](https://github.com/zeit/next.js/blob/canary/examples/with-styled-components/pages/_document.js)


```react:pages/_document.js
import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'

export default class MyDocument extends Document {
  static async getInitialProps (ctx) {
    const sheet = new ServerStyleSheet()

    const originalRenderPage = ctx.renderPage
    ctx.renderPage = () => originalRenderPage({
      enhanceApp: App => props => sheet.collectStyles(<App {...props} />)
    })

    const initialProps = await Document.getInitialProps(ctx)
    return { ...initialProps, styles: [...initialProps.styles, ...sheet.getStyleElement()] }
  }
}
```



以上でstyled-componentsでの開発ができるようになります。

```pages/index.js
import React from 'react'
import styled from 'styled-components'

const Title = styled.h1`
  color: red;
  font-size: 50px;
`

export default () => <Title>My page</Title>
```

### Globalなスタイル
`createGlobalStyle`や、`ThemeProvider`など全体に関わるものは`_app.js`に書くことでページ全体に適用されます。

```react:pages/_app.js
import React from "react";
import App, { Container } from "next/app";
import { createGlobalStyle, ThemeProvider } from "styled-components";

const GlobalStyle = createGlobalStyle`
  body {
    background: ${p => p.theme.backgroundColor};
    color: red;
  }
`;

export default class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props;
    return (
      <ThemeProvider theme={{ backgroundColor: "yellow" }}>
        <Container>
          <GlobalStyle />
          <Component {...pageProps} />
        </Container>
      </ThemeProvider>
    );
  }
}
```

## emotion

```shell:shell
npm i --save @emotion/core @emotion/styled babel-plugin-emotion
```

```json:.babelrc
{
  "presets": [
    "next/babel"
  ],
  "plugins": [
    ["emotion"]
  ]
}
```

emotionはv10からno configでSSR対応までしてくれていますので以上で設定が完了です。
スタイルを当てた例が以下になります。

```react:pages/index.js
/** @jsx jsx */
import { jsx, css } from "@emotion/core";
import styled from "@emotion/styled";

const style = css`
  color: hotpink;
`;

const StyledParagraph = styled.p`
  color: red;
  font-size: 40px;
`;

export default () => (
  <div css={style}>
    Welcome to
    <StyledParagraph>Next.js!</StyledParagraph>
  </div>
);
```

### Globalなスタイル
例に漏れず、`pages/_app.js`にGlobalコンポーネントを入れることで、ページ全体に適用されるスタイルが書けます。

```react:pages/_app.js
import React from "react";
import App, { Container } from "next/app";
import { css, Global } from "@emotion/core";

export default class MyApp extends App {
  render() {
    const { Component, pageProps } = this.props;

    return (
      <Container>
        <Global
          styles={css`
            body {
              background: green;
            }
          `}
        />
        <Component {...pageProps} />
      </Container>
    );
  }
}
```

# おわりに
CSSは、たかがCSSされどCSSだと思います。プロジェクトに合ったCSSフレームワークを選んでいきましょう。