【React】emotion/css の使い方メモ

<!-- ==================================================== -->
# 概要
<!-- ==================================================== -->
ReactやNext.jsを使って開発をする際に、**emotion/css**を使用しています。

とても使い勝手がいいのですが、先人の方のスタイリングを参考にしたいと思ったときに、どうしてもバニラの`HTML + CSS`を、`React + emotion/css`仕様に直す工程が発生します。

大体の場合、ほぼ差異なく転写することができるのですが、CSS in JSの性質上すこし書き方に癖がある部分があったのでまとめました。

詳しく知りたい方は、是非公式ドキュメントを読みましょう。英語ですがとてもわかりやすく書いてあります。

https://emotion.sh/docs/@emotion/css

<!-- ==================================================== -->
# 基本形（css）
<!-- ==================================================== -->
**css``**でスタイリングしていきます。
プロパティ名はCSSと同じです。また、VSCodeだと**コード補完がきく**ので記述が楽です。

```.tsx
import React, { VFC } from 'react';
import { css } from '@emotion/css';

export const Note: VFC = () => {
	return (
		<div className={styles.container}>
			<div></div>
		</div>
	)
}

const styles = {
	container: css`
		position: relative;
		width: 100vw;
		height: 100vh;
		background-color: #1e1e1e;
		display: flex;
		justify-content: center;
		align-items: center;
	`
}
```
* class名は自動で割り振られます。
![スクリーンショット 2021-09-25 010643.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/1884c135-984e-5fe8-7c15-086b3cbde7b4.png)

<!-- ==================================================== -->
# 疑似クラス・疑似要素（:hover・::before）
<!-- ==================================================== -->
**&**のあとに`:hover`や`::after`、`::before`などを記述します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/9f280cd8-d48d-a060-86ff-5948e8f609f0.png" width="500">

```.tsx
import React, { VFC } from 'react';
import { css } from '@emotion/css';

export const Note: VFC = () => {
	return (
		<div className={styles.container}>
			<div className={styles.box}></div>
		</div>
	)
}

const styles = {
	container: css`
		position: relative;
		width: 100vw;
		height: 100vh;
		background-color: #1e1e1e;
		display: flex;
		justify-content: center;
		align-items: center;
	`,
	box: css`
		position: relative;
		width: 300px;
		height: 300px;
		background-color: rgba(255, 0, 0, 0.5);

		&:hover {
			background-color: rgba(0, 0, 255, 0.5);
		}
		&::after {
			content: '';
			position: absolute;
			top: 0;
			width: 100%;
			border-top: 5px solid red;
		}
		&::before {
			content: '';
			position: absolute;
			bottom: 0;
			width: 100%;
			border-top: 5px solid blue;
		}
	`
}

```

<!-- ==================================================== -->
# スタイルの統合（cx）
<!-- ==================================================== -->
**cx**を使用してクラスを統合します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/4f2f0746-c77b-a755-de55-43e60876379c.png" width="500">

```.tsx
import React, { VFC } from 'react';
import { css, cx } from '@emotion/css';

export const Note: VFC = () => {
	return (
		<div className={styles.container}>
			<div className={cx(styles.box, styles.red)}></div>
			<div className={cx(styles.box, styles.blue)}></div>
		</div>
	)
}

const styles = {
	container: css`
		position: relative;
		width: 100vw;
		height: 100vh;
		background-color: #1e1e1e;
		display: flex;
		justify-content: center;
		align-items: center;
	`,
	box: css`
		position: relative;
		width: 300px;
		height: 300px;
	`,
	red: css`
		background-color: rgba(255, 0, 0, 0.5);
	`,
	blue: css`
		background-color: rgba(0, 0, 255, 0.5);
	`
}
```

<!-- ==================================================== -->
# メディアクエリ（media）
<!-- ==================================================== -->
バニラのCSSと同様に、**＠media**のあとに記述します。

```.tsx
import React, { VFC } from 'react';
import { css } from '@emotion/css';

export const Note: VFC = () => {
	return (
		<div className={styles.container}>
			<div className={styles.box}></div>
		</div>
	)
}

const styles = {
	container: css`
		position: relative;
		width: 100vw;
		height: 100vh;
		background-color: #1e1e1e;
		display: flex;
		justify-content: center;
		align-items: center;
	`,
	box: css`
		position: relative;
		width: 300px;
		height: 300px;
		background-color: rgba(255, 0, 0, 0.5);

		@media (max-width: 1000px) {
			background-color: rgb(0, 0, 255, 0.5);
		}
	`
}
```

<!-- ==================================================== -->
# アニメーション（keyframes）
<!-- ==================================================== -->
**keyframes``**で作成したアニメーションを**${}**で参照します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/1fa5732f-57df-ed2b-b46e-e3cf0b349e11.png" width="300">

```.tsx
import React, { VFC } from 'react';
import { css, keyframes } from '@emotion/css';

export const Note: VFC = () => {
	return (
		<div className={styles.container}>
			<div className={styles.box}></div>
		</div>
	)
}

const animations = {
	rotate: keyframes`
		0% {
			transform: rotate(0deg)
		}
		100% {
			transform: rotate(360deg)
		}
	`
}

const styles = {
	container: css`
		position: relative;
		width: 100vw;
		height: 100vh;
		background-color: #1e1e1e;
		display: flex;
		justify-content: center;
		align-items: center;
	`,
	box: css`
		position: relative;
		width: 300px;
		height: 300px;
		background-color: rgba(255, 0, 0, 0.5);
		animation: ${animations.rotate} 10s linear infinite;
	`
}
```

<!-- ==================================================== -->
# classの動的な追加による遷移（transition）
<!-- ==================================================== -->
emotion/cssを使用すると、class名は自動的に生成されます。
`classList.toggle`などで動的に明示的なclassを追加して、それを起点に**transition**させたい場合は、以下のように記述します。

![output(video-cutter-js.com) (4).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/b4ed2bee-461d-2766-3893-01256d12b5d8.gif)

**&**のあとにclass名（例の場合、.toggle）を記述しています。

```.tsx
import React, { VFC, useRef } from 'react';
import { css } from '@emotion/css';

export const Note: VFC = () => {
	const boxRef = useRef<HTMLDivElement>(null)

	const clickHandler = () => {
		if (boxRef.current) {
			boxRef.current.classList.toggle('toggle')
		}
	}

	return (
		<div className={styles.container}>
			<div ref={boxRef} className={styles.box} onClick={clickHandler}></div>
		</div>
	)
}

const styles = {
	container: css`
		position: relative;
		width: 100vw;
		height: 100vh;
		background-color: #1e1e1e;
		display: flex;
		justify-content: center;
		align-items: center;
	`,
	box: css`
		position: relative;
		width: 300px;
		height: 300px;
		background-color: rgba(255, 0, 0, 0.5);
		transition: ease-in-out 3s;

		&.toggle {
			border-radius: 50%;
			transform: rotate(360deg) scale(0.5);
		}
	`
}
```

**スタイルの統合（cx）を使った書き方**
スタイルの統合（cx）を使用すると、以下のように書くこともできます。

```.tsx
import React, { useState, VFC } from 'react';
import { css, cx } from '@emotion/css';

export const Note: VFC = () => {
	const [toggle, setToggle] = useState(false)

	const clickHandler = () => {
		setToggle(!toggle)
	}

	return (
		<div className={styles.container}>
			<div
				className={cx(styles.box, { [styles.boxTrasition]: toggle })}
				onClick={clickHandler}>
			</div>
		</div>
	)
}

const styles = {
	container: css`
		position: relative;
		width: 100vw;
		height: 100vh;
		background-color: #1e1e1e;
		display: flex;
		justify-content: center;
		align-items: center;
	`,
	box: css`
		position: relative;
		width: 300px;
		height: 300px;
		background-color: rgba(255, 0, 0, 0.5);
		transition: ease-in-out 3s;
	`,
	boxTrasition: css`
		border-radius: 50%;
		transform: rotate(360deg) scale(0.5);
	`
}
```
* `cx(styles.box, { [styles.boxTrasition]: toggle })`で、`toggle `の状態に応じて`styles.boxTrasition`の追加/削除を切り替えます。

<!-- ==================================================== -->
# 引数
<!-- ==================================================== -->
公式な使い方ではないですが、**css``**に引数を取ることもできます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/9ad0ac93-6680-ffb4-6905-368eccc5fd2b.png" width="500">

keyframesを参照するときと同様に、変数は**${}**で参照します。

```.tsx
import React, { VFC } from 'react';
import { css } from '@emotion/css';

export const Note: VFC = () => {
	return (
		<div className={styles.container}>
			<div className={styles.box('rgb(222, 84, 71)')}></div>
			<div className={styles.box('#25AFF3')}></div>
		</div>
	)
}

const styles = {
	container: css`
		position: relative;
		width: 100vw;
		height: 100vh;
		background-color: #1e1e1e;
		display: flex;
		justify-content: center;
		align-items: center;
	`,
	box: (bgColor: string) => css`
		position: relative;
		width: 300px;
		height: 300px;
		background-color: ${bgColor};
	`
}
```

<!-- ==================================================== -->
# おまけ（記述のルール）
<!-- ==================================================== -->
試行錯誤して、個人的にコーディングしやすく可読性の良いと思った記述のルールです。

### スタイルの記述位置
スタイルはコンポーネントの下に書きます。
コードを見る人の多くが一番興味があるのは、コンポーネントだからです。

```.tsx
import React, { VFC } from 'react';
import { css } from '@emotion/css';

export const Note: VFC = () => {
	return (
		<div className={styles.container}>
			<div></div>
		</div>
	)
}

const styles = {
	container: css`
		position: relative;
		width: 100vw;
		height: 100vh;
		background-color: #1e1e1e;
		display: flex;
		justify-content: center;
		align-items: center;
	`
}
```

### コンポーネントのパラメーター記述位置
`className`は、先頭の方に書きます。
重要なプロパティ・変動しないプロパティを先頭側へ、変動するプロパティを末尾側へ記述します。

```.tsx
<Component key={i} id="box" className={styles.box} {...props} onClick={clickHandler} />
```
* `{...props}`は、カスタムコンポーネントへ渡すプロパティです（text="aaa" など）。1個渡すこともあれば5個渡すこともあるので、末尾側です。
* `event`は、一番後ろです。eventも複数個ある場合があり、またプロパティの文字数が長くなるためです。