【React】CSS-in-JSのライブラリEmotionを試してみる
概要
ReactでCSSを使う方法はいくつかあるが、急にemotionを使うことになったのでメモ。
「Emotion」はJavaScriptでCSSを記述するために作られたCSS-in-JSのライブラリ。
CSS-in-JSは、かじったことあるが、正直わかりずらい。
/* styled-componentsの例*/
const Wrapper = styled.h1`
width:800px;
margin:auto;
`
const Title = styled.h1`
color: red;
`
<Wrapper>
<Title>Styled Components</Title>
</Wrapper>
上記を見ての通り、styled-componentsでは「h1」や「div」などのhtmlのタグを、「Title」や「Wrapper」というタグで記述することになるから、このタグなんだっけ??となったらわざわざCSSの記述を行ったり来たりしないといけないから混乱するんですよね。
それに比べてEmotionはこれまでのCSSのように書くことができる。
Emotionを使うメリット
実際にEmotionを使うと良いと感じた点は、
- タグが分かりやすい
- styled-componentsとしての書き方もできる。
以下、Emotionで書くとタグが分かりやすいという例
<div css={wrapper}>
<h1 css={title}>Styled Components</h1>
</div>
Reactだと「className」と書くところが、「css」になっていますが、通常のCSSのようにクラス名を指定するようなイメージなので分かりやすい。
Emotionの使い方
実際にEmotionのインストールから使いかたの流れ。
インストールと読み込み
使えるようにするにはターミナルで下記のコマンドを打つ。
//フレームワークにとらわれず、Emotionを使用するためのパッケージ。
npm install @emotion/css
//React用のEmotionパッケージ。
npm install @emotion/react
今回はReactで使用したいので、React用のパッケージを使う。
インストールが終わったら、importで読み込む。
import { css } from "@emotion/react";
公式サイトなど見ると普通にimportで使えるようだが、なぜか使えないという情報もちらほら。。
どうやらEmotionを使う場合はimport前に1行コードを追加する必要がありそうです。
もしあれ?CSSが効いてないかもと思ったら下記も追加してみると効くかも。
「JSX pragma」という記述らしいです。
/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react";
TypeScriptの場合
typescriptの場合も上記と同様に記載しますが、css
というプロパティだと認識されずエラーが出ることがあります。
その場合はtscoinfig.json
に下記を記載。
{
"compilerOptions": {
.
.
.
"jsx": "react-jsx",
"types": ["@emotion/react/types/css-prop"], // 追記
},
"include": ["src"]
}
追記
Gatsby + typescript(プロジェクト作成時にEmotionを選択)の場合、/** @jsxImportSource @emotion/react */
を記載すると逆にエラーが出て、消したら使えました。
Emotionの書き方
実際にEmotionの書き方として2種類あります。
Emotoinでもstyled-components
の書き方ができますが、@emotion/styled
をインストールする必要がある。今回はEmotionの書き方についてメモなのでstyled-components
の書き方については省略する。
Emotionの場合、CSSの指定はclassName
ではなくcss
になる。
ストリングスタイル
バッククォートを使った書き方。この書き方が一番、これまでのCSSの書き方に近い。
ただ、予測変換が出ないので、vscodeでvscode-styled-components
を入れると予測変換が使えるようになる。
const Wrapper = css`
width:100%;
max-width:800px;
margin:auto;
`;
function App() {
render(
<div css={Wrapper}>
<p>ストリングススタイル</p>
</div>
)
}
オブジェクトスタイル
オブジェクトスタイルの場合は、以下のようになる。
- CSSプロパティがキャメルケースになる
- 値は文字列として書く
- プロパティを複数設定する際は、セミコロンではなくカンマで区切る
const Wrapper = css({
width:"100%",
maxWidth:"800px",
margin:"auto",
})
function App() {
render(
<div css={Wrapper}>
<p>ストリングススタイル</p>
</div>
)
}
インラインで指定する
可読性はなくなるのでインラインでは、書くことはあまりないと思うが、書くことも可能。
function App() {
render(
<>
<p css={css`font-size:30px; color:red;`}>インライン</p>
</>
)
}
擬似要素やメディアクエリー
疑似要素やメディアクエリーもSCSSのように使うことができる。
{/* ストリングススタイル */}
const rollOver = css`
font-weight: bold;
&:hover {
opacity: 0.5;
}
&::before{
content: "●";
display: inline-block;
}
@media (max-width: 767px) {
font-size: 8vw;
}
`
{/* オブジェクスタイル */}
const rollOverObj = css({
fontWeight: "bold",
fontSize: "20px",
"&:hover": {
opacity: 0.5,
},
"&::before": {
content: '"●"',
display: "inline-block",
},
"@media (max-width: 767px)":{
fontSize: "8vw",
}
});
function App() {
render(
<>
<p css={rollOver}>ロールオーバー</p>
<p css={rollOverObj}>ロールオーバー:オブジェクト</p>
</>
)
}
オブジェクト形式で各場合の注意点として、content
プロパティは「""(ダブルクォーテーション)」で括る必要があるがさらに「''(シングルコーテーション)」で括る必要がある。
そうでないとエラーになる。
これは通常のCSSでダブルクォーテーションで括るが、Emotionの場合は、オブジェクト形式の場合は値をダブルクォーテーション(もしくはシングルコーテーション)で括る必要があるので、エラーになる。
CSSを結合する
Emotionでは、CSSを複数定義して、結合することができる。
配列で複数のクラスを指定
複数のクラスを指定するには、配列のように指定すればいい。
こちらはオブジェクトスタイルやストリングスタイルなど関係なく同じ。
const box = css`
width: 300px;
height: 300px;
margin: auto;
`
const bgColorRed = css`
background-color: red;
`
function App() {
render(
<div css={[box, bgColorRed]}>ここはボックス</div>
)
}
他にもCSS通し結合することができる
ちなみにEmotionにはCXという書き方があるらしいが、その場合はパッケージが違う??ようで、Reactの場合は「@emotion/react
」だが通常のEmotionの場合は「@emotion/css
」というhPaでないとCXという書き方は使えないっぽい。
ストリングスタイル
変数代入で結合。
const bgColorRed = css`
background-color: red;
`
const box = css`
/* 追加 */
${bgColorRed}
width: 300px;
height: 300px;
margin: auto;
`
function App() {
render(
<div css={box}>ここはボックス</div>
)
}
オブジェクトスタイル
スプレッド演算子で結合できるらしいのだが、効かなかったので原因が分からず。
const bgColorRed = css`
background-color: red;
`
const box = css({
...bgColorRed,
width: "300px";
height: "300px";
margin: "auto";
})
function App() {
render(
<div css={box}>ここはボックス</div>
)
}
入れ子
親要素にCSSを指定してその子要素に対しても指定できる。Sassみたいな使い方も可能。
ストリングススタイル
const listStyle=css`
display: flex;
justify-content: center;
list-style: none;
li{
background-color: blue;
margin: 0 10px;
}
`
function App() {
render(
<ul css={listStyle}>
<li>リスト</li>
<li>リスト</li>
<li>リスト</li>
</ul>
)
}
オブジェクトスタイル
const listStyle = css({
display: "flex",
justifyContent: "center",
listStyle: "none",
li: {
backgroundColor: "red",
margin: "0 10px",
},
});
function App(){
render(
<ul css={listStyle}>
<li>リスト</li>
<li>リスト</li>
<li>リスト</li>
</ul>
)
}
コンポーネントにProps経由で渡す
cssを親から受け取りたいコンポーネントはprops経由で渡す。
注意点としてコンポーネントで受け取る場合はclassName
にしてあげる。
例えば、mycss
とか適当につけると反映されない。
const buttonWrapperStyle = css`
background-color: #ffff00;
`
const ButtonComponet = ({className, children}) => (
<button className={className}>{children}</button>
)
function App(){
render(
<ButtonComponet css={buttonWrapperStyle} >ボタンだよ</ButtonComponet>
)
}
グローバルスタイルを設定する
何かしら全体に関わるCSSを設定したい場合などで使う。
ただ使いすぎるのは注意。Emotionの全てのクラス名が一意であるという利点が失われてしまうらしい。
import { css, Global } from "@emotion/react";
const globalBody = css`
body {
margin: 0;
background: #cfcfcf;
}
`
function App() {
const [clicked, setClicked] = useState(false);
const clickHandler = () => {
setClicked(!clicked);
};
return (
<>
<Global styles={globalBody} />
<h1>タイトル</h1>
</>
);
}
Global
をインポートする。
globalBody
という変数名でbodyタグのCSSを設定する。
<Global>
タグを使ってstyles
にCSSを設定する。
これまではcss
の属性だったが、<Global>
の場合はstyles
となる。
まとめ
取り急ぎ、書き方だけは分かった。
Discussion