React: styled-componentsでスタイルをJavaScriptの中に定める

Reactは、マークアップ(テンプレート)をJSXの構文で、JavaScript(JS)ファイルに書くのが原則です(「[JSXの導入](https://ja.reactjs.org/docs/introducing-jsx.html)」参照)。けれど、スタイルシートは、CSSファイルに定めます。これもJavaScriptコードに書いてしまえれば、マークアップもスタイルもコンポーネントごとにひとつのファイルにまとめられてすっきりするでしょう。

Reactでスタイルを割り当てるやり方はいくつかあります(「[CSSとスタイルの使用
](https://ja.reactjs.org/docs/faq-styling.html)」)。その中で「[CSS-in-JS](https://ja.reactjs.org/docs/faq-styling.html#what-is-css-in-js)」と呼ばれるのが、JavaScriptコードでCSSを定める手法です。[styled-components](https://medium.com/styled-components/styled-components-getting-started-c9818acbcbbd)は、そのためのライブラリとしてもっとも注目されています。本稿では、styled-componentsのインストールから基本的な使い方までをご紹介しましょう。


# create-react-appでひな形のReactアプリケーションをつくる
styled-componentsはプロジェクトにインストールすることにします。そこで、今回使うのは[`create-react-app`](https://facebook.github.io/create-react-app/)コマンドです(「[新しいReactアプリを作る](https://ja.reactjs.org/docs/create-a-new-react-app.html)」参照)。コマンドラインツールからつぎのように入力して、アプリケーションのひな形をつくります。

```terminal
$ npx create-react-app my-app
```
コマンドに与えた`my-app`という名前のフォルダがつくられ、依存関係を含めた必要なファイルがつぎのように納められます(図001)。

#### 図001■Reactアプリケーションのひな形としてつくられたファイル
![qiita_12_002_003.png](https://qiita-image-store.s3.amazonaws.com/0/262045/a806aca1-77e9-99aa-df5e-ad01418c0d0f.png)

これですぐにひな形のReactアプリケーションを、ローカルサーバーで開くことができます。つぎのようにアプリケーションのディレクトリ(`my-app`)に移って、コマンド`npm start`を打ち込んでください。

```terminal
$ cd my-app
$ npm start
```

ブラウザのURL[`http://localhost:3000`](http://localhost:3000)でアプリケーションのページが開くでしょう(図002)。

#### 図002■ブラウザで開いたReactアプリケーションのひな形
![qiita_12_002_004.png](https://qiita-image-store.s3.amazonaws.com/0/262045/6e2d425e-205a-d520-0437-129811038bf3.png)


# styled-componentsでCSSを定める
プロジェクトのディレクトリ(`my-app`)に、コマンドラインツールからつぎのように`styled-components`をインストールしてください。

```terminal
$ npm install --save styled-components
```

ひな形にはCSSファイル(`css:src/App.css`)がつくられています。この中のスタイルを順にアプリケーションのJavaScriptコードに移してゆきましょう。まずは、つぎのクラス(`App`)の定めです。

```css:src/App.css
.App {
  text-align: center;
}
```

JavaScriptファイルは、CSSファイル(`App.css`)に替えて、`styled-components`から[`styled`](https://www.styled-components.com/docs/api#styled)を`import`します。スタイルを定めるのは、このヘルパーメソッドです。ドット(`.`)に続けて要素名を添え、文字列でCSSを記述します。[テンプレート文字列``](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/template_strings)を用いれば、スタイルシートと同じ書き方ができて見やすいです。

```js
styled.要素名`CSSの指定`
```

スタイルの定めは、前掲CSSファイルから`{}`の中身を、つぎのようにテンプレート文字列の中にコピーするだけです。そして、このスタイルが納められた変数名(`ReactApp`)で、マークアップの要素をタグづけしてください。

```react:src/App.js
// import './App.css';
import styled from 'styled-components';

const ReactApp = styled.div`
	text-align: center;
`;

class App extends Component {
  render() {
    return (
		// <div className="App">
		<ReactApp>
			<header className="App-header">

			</header>
		</ReactApp>
		// </div>
    );
  }
}
```

CSSファイルの`import`は外したので、すべてのスタイルは除かれたうえで、`styled-components`に移した設定だけが回復しているはずです。同じ要領で、他のスタイルも`styled-components`で移行しましょう。ただし、アニメーションするロゴのスタイルはあとに回します。ここまでのJavaScriptコード(`src/App.js`)をまとめると、つぎのとおりです。

```react:src/App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import styled from 'styled-components';

const ReactApp = styled.div`
	text-align: center;
`;
const AppHeader = styled.header`
	background-color: #282c34;
	min-height: 100vh;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	font-size: calc(10px + 2vmin);
	color: white;
`
const AppLink = styled.a`
	color: #61dafb;
`;

class App extends Component {
	render() {
		return (
			<ReactApp>
				<AppHeader>
					<img src={logo} className="App-logo" alt="logo" />
					<p>
						Edit <code>src/App.js</code> and save to reload.
					</p>
					<AppLink
						href="https://reactjs.org"
						target="_blank"
						rel="noopener noreferrer"
					>
						Learn React
					</AppLink>
				</AppHeader>
			</ReactApp>
		);
	}
}

export default App;
```


# animationプロパティのkeyframes規則を定める
回転するロゴ(`<img>`)のスタイルには、`animation`プロパティに[`@keyframes`規則](https://developer.mozilla.org/ja/docs/Web/CSS/@keyframes)が与えられています。これを扱うヘルパーメソッドは、[`keyframes`](https://www.styled-components.com/docs/api#keyframes)です。これをつぎのように`styled-components`から`import`しておきます。テンプレート文字列への書き方は、スタイルシートと同じくCSSのままです。この規則を納めた変数(`AppLogoSpin`)は、`animation`の定めに、[プレースホルダー`${}`](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/template_strings#Description)で組み込んでください。これで、回転するロゴのスタイルも割り当てられました。

```react:src/App.js
import styled, { keyframes } from 'styled-components';

const AppLogoSpin = keyframes`
	from {
		transform: rotate(0deg);
	}
	to {
		transform: rotate(360deg);
	}
`;
const AppLogo = styled.img`
	animation: ${AppLogoSpin} infinite 20s linear;
	height: 40vmin;
`;

class App extends Component {
	render() {
		return (
			<ReactApp>
				<AppHeader>
					<AppLogo src={logo} alt="logo" />

				</AppHeader>
			</ReactApp>
		);
	}
}
```


# 子要素や擬似クラスにスタイルを割り当てる
styled-componentsサイトの「[Getting started](https://www.styled-components.com/#installation)」を見ただけではわからないのが、子要素や擬似クラスのスタイルです。これらについては、つぎのように現行セレクタを`&`で参照します(「[Supported CSS](https://www.styled-components.com/docs/api#supported-css)」参照)。

```react:src/App.js
const AppHeader = styled.header`

	& code {
		color: #aaeeff;
	}
`
const AppLink = styled.a`

	&:hover {
		color: #00ffff;
	}
`;
```

これで、子要素(`<code>`)や擬似クラス(`:hover`)にスタイルが定められます(図003)。書き上がったJavaScriptコード(`src/App.js`)は、以下のとおりです。

#### 図003■子要素や擬似クラスにスタイルが与えられた
![1901001_001.png](https://qiita-image-store.s3.amazonaws.com/0/128949/d8bacbeb-7a47-992c-83ed-c260d8b55f12.png)


```react:src/App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import styled, { keyframes } from 'styled-components';

const ReactApp = styled.div`
	text-align: center;
`;
const AppHeader = styled.header`
	background-color: #282c34;
	min-height: 100vh;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	font-size: calc(10px + 2vmin);
	color: white;
	& code {
		color: #aaeeff;
	}
`
const AppLogoSpin = keyframes`
	from {
		transform: rotate(0deg);
	}
	to {
		transform: rotate(360deg);
	}
`;
const AppLogo = styled.img`
	animation: ${AppLogoSpin} infinite 20s linear;
	height: 40vmin;
`;
const AppLink = styled.a`
	color: #61dafb;
    &:hover {
        color: #00ffff;
    }
`;

class App extends Component {
	render() {
		return (
			<ReactApp>
				<AppHeader>
					<AppLogo src={logo} alt="logo" />
					<p>
						Edit <code>src/App.js</code> and save to reload.
					</p>
					<AppLink
						href="https://reactjs.org"
						target="_blank"
						rel="noopener noreferrer"
					>
						Learn React
					</AppLink>
				</AppHeader>
			</ReactApp>
		);
	}
}

export default App;
```