ReactやAngular、VueなどでComponentのスタイリング時に抑えておきたいこと


なんとなく自分の中で言語化しておきたかったので、整理も兼ねて記載しておきます🙆🏻‍♂️

普段仕事で様々なAngular、またはReactのコンポーネントを作ったりメンバーから出るPRを読む中で、コンポーネントのスタイルはどういうふうに当てるのが破綻しにくいんだろうと考えていました🤔
Angularは良くも悪くも一つのComponentが結構おっきくなりがちだったのでそこまで意識しなかったですが、Reactは何なら分割しないと気持ち悪いとすら思えるくらいにコンポーネントを分割しやすいです。
コンポーネントを分割することは各ファイルごとに把握すべき事柄が減るので基本的にはいいことだと思っていますが、スタイリングについては意識しないと破綻してしまうなーと思っています。
(もちろん、スタイリングに限らず意識しないと破滅するんだけど、今回はスタイルについての話です)

スタイルの破綻っていうのは、ロジックの破綻と同じで、どこで何があたっているか把握できないみたいなことや、利用側からどうやってそのコンポーネントを使えばいいのかわからなかったり、必要以上にスタイリング用のたくさんのpropsを貰う必要が出てくる、みたいなのもそうかなと思っています。

そこでいくつか守ったほうが良さそうなパターンがあるので、それを列挙していきます💪

■ 外に対してのmarginは持たない

これはMust💪
例えば以下のようなことはNG

const StyledButton = styled.button`
  margin: 16px auto;
`;

export const Button: React:FC = () => <Button>ボタン</Button>

なぜNGか

このButtonは自分がどこで使われているか知らないはずなのに外側の隙間を定義するmarginを持ってしまっています。
子はそんなことは考えなくって良くて、どこに配置するかは使う側(親コンポーネント)が考えれば良いとおもいます🙆🏻‍♂️
逆にこういうstyleを持ってしまうと、「違うmargin渡したいから、marginもpropsに追加しなきゃ、、、」みたいになりがちです。
同様の理由で、position: absoluteなんかも自身が持たないようにしましょう(親のコンテキストによって位置が決まるので)

🤔 paddingはどうですか?

paddingは持っても大丈夫です。paddingはコンポーネント自身の内部の隙間です。
例えるなら、paddingというのは脂肪です。marginはソーシャルディスタンスです。自身のことはそのコンポーネントが知っていて大丈夫なので、paddingは問題ありません🙆🏻‍♂️

🙆🏻‍♂️

const Parent: React:FC = () => (
  <div>
    <Button css={{ marginBottom: '16px' }} />
    <Button />
  </div>
);

みたいに使う側で指定してあげましょう。

■ widthは特別な事情がない限り指定しない

widthをpropsとして渡せるコンポーネントを見る機会が結構あります👀
ですが、widthはそもそも、親のコンテキストで決まるので、多くの場合widthを指定する必要はない気がしています👀
というより、display: blockであれば、そもそもwidthは100%になっているはずです。
widthが100%の状態であれば、使う親の方でdivで囲むなりして幅を指定してやることが可能です。

ただ、そのコンポーネント特有のwidth、またはmax-widthがある場合もあります。
例えばボタンや、バッジなどです。こういうものに関しては、width、またはmax-widthを持つことになると思います。
そもそも、そういうコンポーネントは自身のサイズが決められているので、あまりwidthを変更したいと言う機会がない気がしますが、もし必要になる場合はパターンがあると思うので、本当にpropsでwidthpx単位とかで渡すのではなく、サイズを表す定数(たとえばlargeやあるいは使われるコンテキストの名前など)を定義してそれを渡すほうが良さそうです。
もし定義するのが辛くなるくらい大量に定数が発生する場合はもしかしたらデザイナーと相談したほうがいいかもしれません。

■ レイアウトに関するスタイル、またはタグはできる限り全体を見通せるように指定する

いいたいことはこれです(毎度非常に良い記事ばかりで勉強になっています🙇‍♂️)。

https://blog.uhy.ooo/entry/2020-10-15/react-paired-css/

例えば、flexdisplay: flexの子要素として初めて意味を成すスタイルです。
なので

const StyledWrapper = styled.div`
  display: flex;
  & > .hoge {
     flex: 〇〇;
  }
`;

のようにまとめて書くと非常に全体を把握しやすいです。
もちろん様々あると思うので必ずこの方法​で記述しろとは言いませんが、少なくとも1ファイル内で追えるようにすべきです👀

また、これはhtmlタグについても同様です。
thtd、またはlidtddなどは親要素がいないと意味をなさない要素です(逆もまたしかりです)。
それらは必ず1ファイル内で追えるようにし、単体でexportしないようにしましょう🙆🏻‍♂️

■ 【要検討】 displayはinline系にしない

これは微妙に賛否が分かれると思うのですが、基本的にコンポーネント自体のスタイルとしてはdisplay: blockまたはflex, tableなど、インラインでないほうがいいのかなと思っています👀
理由はinlineinline-block等のインライン要素は記述コンテンツ => 文章とその中に含まれるマークアップとして定義されているものが持つスタイルで、使用されるコンテキストとしてもinlineであることを期待してしまっているからです。

https://developer.mozilla.org/ja/docs/Web/HTML/Block-level_elements#Block-level_vs._inline

コンポーネントを考えたときに、コンポーネントはなにかのパーツではありますが、それ自身で完結しているものでもあります🤔
そうだとするならば、コンポーネントのdisplayがインラインであるのはコンポーネントとして不自然な状態だと言えるのではないかなーと思っています👀

また、使う側からすると、各コンポーネントがblock、またはそれに準じたdisplayになっていることで、どのコンポーネントもある程度似たように扱うことができるようになります。
flex等の指定が可能になったことで、コンポーネントがinlineでないと実現できないレイアウトもほとんどなくなったと言う認識です。

ただ、これについてはwebcomponentsのCustom elementsのissueでデフォルトのスタイルをdisplay: blockにすべきだという議論があったものの、この意見を補強する資料が見つかりませんでした🙄

https://github.com/WICG/webcomponents/issues/224

なので、僕にとってはこのほうがやりやすいが、そこまで気にしなくていいことなのかもしれません👀
この辺こうするといいとかあればぜひご意見お待ちしています🙏


以上です🌮
他にもグッドなプラクティスあればぜひ教えて下さい🙏