😽

【React】CSS-in-JSのライブラリEmotionを試してみる

2023/01/30に公開

概要

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で読み込む。

App.js
import { css } from "@emotion/react";

公式サイトなど見ると普通にimportで使えるようだが、なぜか使えないという情報もちらほら。。
どうやらEmotionを使う場合はimport前に1行コードを追加する必要がありそうです。
もしあれ?CSSが効いてないかもと思ったら下記も追加してみると効くかも。
「JSX pragma」という記述らしいです。

App.js
/** @jsxImportSource @emotion/react */
import { css } from "@emotion/react";

TypeScriptの場合

typescriptの場合も上記と同様に記載しますが、cssというプロパティだと認識されずエラーが出ることがあります。
その場合はtscoinfig.jsonに下記を記載。

tsconfig.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を入れると予測変換が使えるようになる。

App.js
const Wrapper = css`
  width:100%;
  max-width:800px;
  margin:auto;
`;

function App() {
 render(
  <div css={Wrapper}>
   <p>ストリングススタイル</p>
  </div>
)
}

オブジェクトスタイル

オブジェクトスタイルの場合は、以下のようになる。

  • CSSプロパティがキャメルケースになる
  • 値は文字列として書く
  • プロパティを複数設定する際は、セミコロンではなくカンマで区切る
App.js
const Wrapper = css({
  width:"100%",
  maxWidth:"800px",
  margin:"auto",
  })
function App() {
 render(
  <div css={Wrapper}>
   <p>ストリングススタイル</p>
  </div>
)
}

インラインで指定する

可読性はなくなるのでインラインでは、書くことはあまりないと思うが、書くことも可能。

App.js
function App() {
 render(
  <>
  <p css={css`font-size:30px; color:red;`}>インライン</p>
  </>
 )
}

擬似要素やメディアクエリー

疑似要素やメディアクエリーもSCSSのように使うことができる。

App.js

{/* ストリングススタイル */}
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を複数定義して、結合することができる。

https://emotion.sh/docs/composition

配列で複数のクラスを指定

複数のクラスを指定するには、配列のように指定すればいい。
こちらはオブジェクトスタイルやストリングスタイルなど関係なく同じ。

App.js
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という書き方は使えないっぽい。

ストリングスタイル

変数代入で結合。

App.js
const bgColorRed = css`
  background-color: red;
`
const box = css`
/* 追加 */
${bgColorRed}
  width: 300px;
  height: 300px;
  margin: auto;
`
function App() {
 render(
  <div css={box}>ここはボックス</div>
)
}

オブジェクトスタイル

スプレッド演算子で結合できるらしいのだが、効かなかったので原因が分からず。

App.js
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みたいな使い方も可能。

ストリングススタイル

App.js
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>
)
}

オブジェクトスタイル

App.js
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とか適当につけると反映されない。

App.js
const buttonWrapperStyle = css`
  background-color: #ffff00;
`
const ButtonComponet = ({className, children}) => (
  <button className={className}>{children}</button>
)

function App(){
 render(
  <ButtonComponet css={buttonWrapperStyle} >ボタンだよ</ButtonComponet>
  )
}

グローバルスタイルを設定する

何かしら全体に関わるCSSを設定したい場合などで使う。
ただ使いすぎるのは注意。Emotionの全てのクラス名が一意であるという利点が失われてしまうらしい。

App.js
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となる。

https://emotion.sh/docs/globals

まとめ

取り急ぎ、書き方だけは分かった。

https://emotion.sh/docs/introduction
https://ralacode.com/blog/post/react-emotion/
https://qiita.com/cheez921/items/1d13545f8a0ea46beb51
https://scrawledtechblog.com/emotion_use/

Discussion