本文では、React プロジェクトで CSS in JS ソリューションである emotion を使用する方法について説明します。
emotion#
emotion は、高性能で柔軟な CSS-in-JS ライブラリです。フレームワークに依存せず、vue や react で使用できます。現在、私たちは react を使用しており、複数のプロジェクトが本番環境で安定して稼働しています。emotion11 は emotion10 の若干の改善であり、主に開発者の体験、TS の型の改善、そして新しいバージョンのパーサーである Stylis の使用に重点を置いています。この記事では、React と TypeScript で emotion11 を統合する方法について説明します。
変更点#
パッケージの名前変更#
emotion11 の最も重要な変更の 1 つは、ユーザー向けのパッケージのほとんどが名前を変更したことです。
名前変更されたパッケージのリスト:
- @emotion/core → @emotion/react
- emotion → @emotion/css
- emotion-theming → @emotion/react に移動
- emotion-server → @emotion/server
- create-emotion → @emotion/css/create-instance
- create-emotion-server → @emotion/server/create-instance
- babel-plugin-emotion → @emotion/babel-plugin
- eslint-plugin-emotion → @emotion/eslint-plugin
jest-emotion → @emotion/jest
Hooks#
内部でフックを使用してパッケージのサイズを最適化し、React DevTools でより良い DOM ツリーを表示します。
TypeScript#
TypeScript の型は完全に書き直されました。#
- emotion を使用する際のビルド時間を短縮し、特に大規模プロジェクトでの効果を発揮します。
- 多くのケースで、emotion コンポーネントに対して手動でジェネリックパラメータを指定する必要がなくなりました。
- props としてのユニオン型がより良くサポートされ、正しく推論されるべきです。
- 無効な型が渡されないように css 関数が制限されました。
- styled のジェネリックパラメータが変更され、ComponentType を指定する場合はそのジェネリックパラメータを削除する必要があります。
- styled はもはや第二のパラメータ ExtraProps を必要とせず、代わりに styled 呼び出しの後に移動されます。したがって、styled<typeof MyComponent, ExtraProps>(MyComponent) は styled (MyComponent)({}) に書き換えるべきです。
Theme 型#
テーマの型を提供するのがより簡単になりました。以前のようにカスタムインスタンスを作成するのではなく、組み込みの Theme インターフェースを次のように作成できます:
import '@emotion/react'
declare module '@emotion/react' {
export interface Theme {
primaryColor: string
secondaryColor: string
}
}
css prop 型#
使用する JSX ランタイムに基づいて、emotion11 は css prop に対する TypeScript サポートの方法を変更しました。className prop をサポートするコンポーネントにのみ対応する css prop のサポートを追加できます(emotion の JSX ファクトリ関数は提供された css prop を受け取り、それを解析して生成された className をレンダリングされたコンポーネントに渡します)。
Stylis V4#
emotion が使用する css パーサー Stylis がアップグレードされ、長年の解析のエッジケースが修正され、より小さく、より速くなりました。
Emotion のキャッシュ#
高速キャッシュのカスタムインスタンスを作成する際に、現在は key オプションが必要です。それがユニークであること(「css」と等しくないこと)を確認してください。これはスタイルをキャッシュにリンクするために使用されます。複数のキャッシュが同じキーを共有すると、互いのスタイル要素で「争う」可能性があります。新しい prepend オプションにより、Emotion は指定された DOM コンテナの先頭ではなく末尾にスタイルタグを追加できます。
使用方法#
インストール#
yarn add @emotion/react
使用方法#
// このコメントはbabelにjsxをReact.createElementの代わりにjsxという関数への呼び出しに変換するよう指示します
/** @jsx jsx */
import { jsx, css } from '@emotion/react'
const style = css`
color: hotpink;
`
const SomeComponent = ({ children }) => (
<div css={style}>
Some hotpink text.
{children}
</div>
)
const anotherStyle = css({
textDecoration: 'underline'
})
const AnotherComponent = () => (
<div css={anotherStyle}>Some text with an underline.</div>
)
render(
<SomeComponent>
<AnotherComponent />
</SomeComponent>
)
css prop#
emotion が提供する主要なスタイル記述方法は css prop を使用することであり、コンポーネントにスタイルを設定するための簡潔で柔軟な API を提供します。
css prop を使用する方法は 2 つあります:
Babel Preset#
Babel プリセットは、classic JSX ランタイムを使用している場合に自動的に Emotion の css prop を有効にします。新しい JSX ランタイムを使用する場合は、このプリセットを使用せず、@emotion/babel-plugin を使用してください。
- インストール
yarn add @emotion/babel-preset-css-prop
- 使用方法
.babelrc
{
"presets": [
[
"@emotion/babel-preset-css-prop",
{
"autoLabel": "dev-only",
"labelFormat": "[local]"
}
]
],
}
互換性のある React バージョン(>=16.14.0)を使用している場合、次の設定を使用して新しい JSX ランタイムを選択できます:
.babelrc
{
"presets": [
[
"@babel/preset-react",
{ "runtime": "automatic", "importSource": "@emotion/react" }
]
],
"plugins": ["@emotion/babel-plugin"]
}
JSX Pragma#
CSS prop を使用するソースファイルの先頭に jsx コンパイル指示を設定します。このオプションは、css prop 機能をテストするか、babel 設定を構成できないプロジェクト(create-react-app、codesandbox など)で最適です。
/** @jsx jsx */
linter 設定を含むコメントと同様に、この設定は jsx babel プラグインを React.createElement の代わりに jsx 関数を使用するように構成します。
ゼロ構成ツールを使用してどのランタイム(classic または automatic)を使用するかを自動的に検出している場合、かつ新しい JSX ランタイムを持つ React バージョンを使用している場合(したがってランタイムが自動的に runtime: 'automatic' を構成している場合)、例えば Create React App 4 では
/ ** @jsx jsx * /
コンパイル指示が機能しない可能性があるため、次のように使用する必要があります。
/** @jsx jsx */
import { jsx } from '@emotion/react'
css prop の使用#
/** @jsx jsx */
import { jsx } from '@emotion/react'
render(
<div
css={{
backgroundColor: 'hotpink',
'&:hover': {
color: 'lightgreen'
}
}}
>
This has a hotpink background.
</div>
)
テーマ#
Theme は @ emotion /react パッケージに含まれています。
ThemeProvider をアプリケーションの最上部に追加し、スタイル化されたコンポーネント内で props.theme を使用してテーマにアクセスするか、テーマを css prop として受け取る関数を提供します。
使用方法#
css 関数#
import { ThemeProvider } from '@emotion/react'
const theme = {
colors: {
primary: 'hotpink'
}
}
render(
<ThemeProvider theme={theme}>
<div css={theme => ({ color: theme.colors.primary })}>
some other text
</div>
</ThemeProvider>
)
useTheme フック#
import { ThemeProvider, useTheme } from '@emotion/react'
const theme = {
colors: {
primary: 'hotpink'
}
}
function SomeText (props) {
const theme = useTheme()
return (
<div
css={{ color: theme.colors.primary }}
{...props}
/>
)
}
render(
<ThemeProvider theme={theme}>
<SomeText>some text</SomeText>
</ThemeProvider>
)
型#
テーマの型は型ファイルで宣言する必要があり、そうしないと TS 型エラーが発生します。
types/emotion.d.ts
import "@emotion/react";
declare module "@emotion/react" {
export interface Theme {
colors: {
layoutBodyBackground: string;
headingColor: string;
textColorSecondary: string;
success: string;
warning: string;
error: string;
primary: string;
textColor: string;
};
fontSizes: {
base: number;
};
}
}
tsconfig.json
{
"compilerOptions": {
"paths": {
"*": [
"types/*"
],
},
}
}
まとめ#
CSS in JS のソリューションは、従来の less や sass などに比べて多くの利点があり、emotion の類似ソリューションも多く存在します。私が最も感じたのは、パッケージ速度が大幅に向上し、冗長な CSS が発生しないことですが、CSS の書き方に慣れている人には慣れるまで時間がかかるかもしれません。とにかく、一度試してみる価値があります。