はじめに
社内のプロジェクトで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」を付ける
変数のプレフィックスを統一することで、一目でスタイルを表す変数であると認識しやすくなります。
プレフィックスにはstyle
やcss
などいくつか候補あると思いますが、文字数が短くcssプロパティともイメージづけしやすいcss
をプレフィックスとしてつけると良いと考えます。
また、エディタの補完機能が働き、cssと入力した時点で作成したスタイルの一覧が入力候補として表示されるようになります。
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",
}
]
},
}
親コンポーネントから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;
`;
おわり
今回のプロジェクトは走り出したばっかりなので今後ルールは増えてくると思いますが、最初に決めておくルールはこれぐらいかなと思います。
またルールが増えたり変更することがあれば都度、追記していきたいと思います。