はじめに
先日、EmotoinをNext.jsに導入する記事を書きました。
Next.js 10 + TypeScriptでEmotionを使う
今度は、Emotionをどのように使えるのかを記事にしていきたいと思います。
Emotionを使うと何が嬉しいのかはこちらで記事にしているので、是非こちらも読んでみてください!
Emotionはいいぞ
Emotionを使う
まず、Emotionの基本的な使い方について、解説していきます。
Emotionでは以下のようにCSSを記述することができます。
import React from 'react'
import { css } from '@emotion/react'
// パターン1
const hello = css`
color: red;
`
type Props = {
name: string
}
export const Hello: React.FC<Props> = (props) => {
const { name } = props
return (
<div>
<h1 css={hello}>Hello {name}!</h1>
{/* パターン2 */}
<h1
css={css`
color: blue;
`}
>
Hello {name}!
</h1>
</div>
)
}
helloという変数にバインドしてcssプロパティに指定している方法(パターン1)と、直接cssプロパティに渡している方法(パターン2)です。
この時のhelloにバインドされている値は、以下のようになっています。
const hello = {
map: "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,... */"
name: "1kj5occ-hello"
styles: "color:red;label:hello;"
toString: ƒ _EMOTION_STRINGIFIED_CSS_ERROR__()
}
このオブジェクトをcssプロパティが解釈してcssを生成し、classNameにセットしているという流れです。
label
という見慣れない項目がstyles
にありますが、これはEmotion独自の機能で、このcssの生成するクラス名にlabel
の文字列が結合されて、デバッグしやすくなるというものです。
特に指定しなければ、バインドした変数の名前が設定されます。
パターン2の方では、コンポーネントの名前が設定されます。
Generated class names include the name of the variable or component they were defined in.
とあるように、Babelによって変数の名前が設定されるようになっているので、デバッグがしやすいように直接cssプロパティに指定するのではなく、一度変数にバインドするようにしましょう。
label
に独自の名前を設定して、クラス名に含めることも可能なので、デバッグしやすいように使ってください。
cssの宣言は
const classes = {
// css-8bac6k-hoge
hoge: css``,
// css-1m4255s-test-fuga
fuga: css`
label: test;
`
}
のようにもできるので、このようにしておくと便利でしょう。
Babelの設定でファイル名やディレクトリ名を含むようにすることも可能なので、cssの場所がわかりにくいプロジェクトだと設定をしておくとデバッグがしやすくなるかもしれません。
必要に応じて設定してください。
CSSをマージする
マージと書きましたが、Compositionです。
他で定義したcssのオブジェクトをcssテンプレート文字列の中に埋め込むことができます。
const borderBase = css`
border: 1px solid gray;
border-radius: 5px;
`
const iconButton = css`
${borderBase}
border-radius: 50%;
`
また、cssに配列で指定することで、優先度を指定してクラスを適応することができます。
<button css={[borderBase, iconButton]}>?</button>
細かくは解説しないですが、これによって解決されるCSSの問題点もあります。
気になる人は公式の解説がわかりやすいので、是非読んでみてください。
擬似要素・擬似クラスやメディアクエリを使う
メディアクエリや擬似要素・擬似クラスなども記述することができます。
sassやscssのように、&
をそのクラス名として使うことができるので、以下のように記述できます。
const hoge = css`
&:hover {
color: red;
}
@media (min-width: 420px) {
&:hover {
color: blue;
}
}
`
<div css={hoge}>test</div>
参考:
https://emotion.sh/docs/nested
https://emotion.sh/docs/media-queries
グローバルなスタイルを設定する
グローバルなスタイルが必要な場合もあるでしょう。
その場合は、以下のように設定します。
import { Global, css } from '@emotion/react'
render(
<div>
<Global
styles={css`
.some-class {
color: hotpink !important;
}
`}
/>
<Global
styles={{
'.some-class': {
fontSize: 50,
textAlign: 'center'
}
}}
/>
<div className="some-class">This is hotpink now!</div>
</div>
)
しかし、これを多用してクラス名を宣言するとEmotionの全てのクラス名が一意であるという利点が失われてしまうので気をつけてください。
タグのデフォルトのCSSを打ち消す場合などには便利だと思います。
参考: https://emotion.sh/docs/globals
Themeを使う
ThemeをProviderに設定して使うことができます。
import { ThemeProvider } from '@emotion/react'
const theme = {
colors: {
primary: 'blue'
}
}
render(
<ThemeProvider theme={theme}>
<div
css={(theme) =>
css`
color: ${theme.colors.primary};
`
}
>
hoge
</div>
</ThemeProvider>
)
themeなんてコンスタントな変数で宣言してimportすればいいじゃんと思う人もいるかもしれませんが、ライトテーマとダークテーマを切り替えたい場合などにProviderに設定しておくことで切り替えやすく、便利にthemeを扱えます。
TypeScriptではThemeの型はデフォルトではからのオブジェクト({}
)になっているので、型を宣言してやらないといけません。
import '@emotion/react'
declare module '@emotion/react' {
export interface Theme {
color: string
secondaryColor: string
}
}
このようにTheme型を宣言して、読み込ませましょう。
参考:
https://emotion.sh/docs/theming
https://emotion.sh/docs/emotion-11#theme-type
Object Styleを使う
基本的にはcss
をタグ付きテンプレートリテラルとして使うのが使いやすいと思いますが、オブジェクト形式でCSSを記述することもできます。
Object形式の方がスクリプトから弄りやすいなどのメリットもあるので、必要に応じて使い分けてみてください。
テンプレート文字列に大量の色を埋め込むよりかは、オブジェクト形式で記述した方が可読性が上がる場合などがあるかもしれません。
const hoge = css({
backgroundColor: color
})
参考: https://emotion.sh/docs/object-styles
忘れがちなこと
Emotionを使っていると、コンポーネントの外部から設定したcss
が効かなくて悩むことがあります。(私もこれで小一時間悩みました)
コンポーネントを実装した場合で、css
によるスタイルの拡張を許したい場合は、className
をpropsで受け取って、対象のElementに渡すようにしましょう。
css
プロパティはcss
に渡されたオブジェクトを解釈してclassName
にクラス名を渡すというような挙動をするので、コンポーネント側でclassName
をちゃんと設定しないと機能してくれません。
意外と忘れがちなので、特に変なことをしていないのにcss
が効かないという時があれば、これを確認してみましょう。
参考
まとめ
最後まで読んでいただきありがとうございます。
どうだったでしょうか?
個人的に、これだけ使えればEmotionを使いこなせていると言えるんじゃないかという基準でまとめてみました。
他にも解説しきれていない機能などもありますので、是非公式ドキュメントの方も読んでEmotionを使いこなして行ってください!