thasmto's blog

フロントエンドエンジニアやってます。プログラマーとして学んだことや考えていることを投稿するブログです。

Emotion コーディングルール案

はじめに

社内のプロジェクトでCSSinJSライブラ入りとしてemotionを導入したのでコーディングルールを考えてみました。

プロジェクトで使用する技術

package version
next v11.1.2
typescript v4.4.3
@emotion/react v11.4.1

css関数を使ってスタイルを定義する

emotionではstyle-componentと同様にスタイルを含むコンポーネントを作成するstyled関数と、コンポーネントと独立してスタイルを定義できるcss関数が用意されています。

css関数を使うことで従来のマークアップのようにHTMLとCSSを分別することができるので、css関数を使うことにします。 emotionを導入した理由もcss関数が使えることが大きいです。

import { css, styled } from '@emtion/react';

// styledを使った書き方
const StyleButton = styled.button`
  width: 100px;
`;
const Button = ({ children }) => {
  return <StyledButton>{ children }</styledButton>
}

// cssを使った書き方
const buttonStyle = css`
  width: 100px;
`;
const Button = ({ children }) => {
  return <button css={buttonCss}>{children}</button>
}

cssの記述はファイルの下段に定義する

一つのjsxファイルでコンポーネントのロジック、HTML、CSSを記述するので、それぞれの記述が混在すると可読性が落ちてしまいます。

VueのSFCの記述のように一番下にスタイルを記述することでどこに何が記載されているか予測しやくなります。

参考:レシピサービスのフロントエンドに CSS in JS を採用した話

css関数で作ったスタイルの変数名にプレフィックスとして「css」を付ける

変数のプレフィックスを統一することで、一目でスタイルを表す変数であると認識しやすくなります。 プレフィックスにはstylecssなどいくつか候補あると思いますが、文字数が短くcssプロパティともイメージづけしやすいcssプレフィックスとしてつけると良いと考えます。 また、エディタの補完機能が働き、cssと入力した時点で作成したスタイルの一覧が入力候補として表示されるようになります。 f:id:thasmto:20210925172742p:plain

const Button = ({children}) => {
  return <button css={cssButton}>{ children }</button>
}

const cssButton = css`
  background: #ddd;
  border-radius: 30px;
`;

Highlightの設定

その他にVSCode拡張機能Highlight」を使って、cssから始まる文字の色を変更することで、変数がcss関数で作られたスタイルを意味するのかそれ以外なのか区別しやすくできます。

拡張機能をインストールして、VSCodeの設定ファイルに以下の設定を追加する。 colorは自由に変更してください。

    "highlight.regexes": {
        "(css[a-zA-Z0-9_]+)": {
            "filterFileRegex": ".*(jsx|tsx)$",
            "decorations": [
                {
                    "color": "OldLace",
                }
            ]
        },
    }

f:id:thasmto:20210925172835p:plain

コンポーネントからcssを渡したいときは、子コンポーネント側はclassNameで受け取り、cssプロパティの後ろにclassNameを設定する

emotionを使用すると、cssプロパティで渡したスタイルは内部でclassNameというpropsに変換されて子コンポーネントに渡されます。(参考記事:Emotionを使いこなす) そのため、親から子へスタイルを渡した場合は、子コンポーネントcssプロパティとclassNameプロパティの両方を扱う必要があります。

この二つを同時に指定した場合、classNameプロパティで渡したスタイルの優先度が高くなるのですが、どちらのプロパティを先に記述した方が可読性が高いか考えるときにcssプロパティ内でのマージ方法を参考にします。

cssプロパティ上でスタイルをマージする方法として、配列でcss={[style1, style2]}と記述する方法があります。 この場合、配列の後ろに格納したstyle2の優先順位が高くなります。

このルールに基づき、cssプロパティより優先度が高いclassNameプロパティを後ろに書くことで優先順位をイメージしやすくなります。

const Parent = () => {
  return <div>
    <Child css={cssParentToChild} />
  </div>
}

const Child: React.FC<{ className?: string}> = ({ className }) => {
  return <div css={[cssChild1, cssChild2]} className={className}>child</div>
}
const cssParentToChild = css`
  top: 3px;
`;
const cssChild1 = css`
  top: 1px;
`;
const cssChild2 = css`
  top: 2px;
`;

このように指定すると、以下のようにスタイルが定義されます。 f:id:thasmto:20210925173113p:plain