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でwidth
をpx
単位とかで渡すのではなく、サイズを表す定数(たとえばlarge
やあるいは使われるコンテキストの名前など)を定義してそれを渡すほうが良さそうです。
もし定義するのが辛くなるくらい大量に定数が発生する場合はもしかしたらデザイナーと相談したほうがいいかもしれません。
■ レイアウトに関するスタイル、またはタグはできる限り全体を見通せるように指定する
いいたいことはこれです(毎度非常に良い記事ばかりで勉強になっています🙇♂️)。
例えば、flex
はdisplay: flex
の子要素として初めて意味を成すスタイルです。
なので
const StyledWrapper = styled.div`
display: flex;
& > .hoge {
flex: 〇〇;
}
`;
のようにまとめて書くと非常に全体を把握しやすいです。
もちろん様々あると思うので必ずこの方法で記述しろとは言いませんが、少なくとも1ファイル内で追えるようにすべきです👀
また、これはhtmlタグについても同様です。
th
はtd
、またはli
、dt
、dd
などは親要素がいないと意味をなさない要素です(逆もまたしかりです)。
それらは必ず1ファイル内で追えるようにし、単体でexportしないようにしましょう🙆🏻♂️
■ 【要検討】 displayはinline系にしない
これは微妙に賛否が分かれると思うのですが、基本的にコンポーネント自体のスタイルとしてはdisplay: block
またはflex
, table
など、インラインでないほうがいいのかなと思っています👀
理由はinline
、inline-block
等のインライン要素は記述コンテンツ
=> 文章とその中に含まれるマークアップ
として定義されているものが持つスタイルで、使用されるコンテキストとしてもinlineであることを期待してしまっているからです。
コンポーネントを考えたときに、コンポーネントはなにかのパーツではありますが、それ自身で完結しているものでもあります🤔
そうだとするならば、コンポーネントのdisplay
がインラインであるのはコンポーネントとして不自然な状態だと言えるのではないかなーと思っています👀
また、使う側からすると、各コンポーネントがblock、またはそれに準じたdisplayになっていることで、どのコンポーネントもある程度似たように扱うことができるようになります。
flex等の指定が可能になったことで、コンポーネントがinlineでないと実現できないレイアウトもほとんどなくなったと言う認識です。
ただ、これについてはwebcomponentsのCustom elementsのissueでデフォルトのスタイルをdisplay: block
にすべきだという議論があったものの、この意見を補強する資料が見つかりませんでした🙄
なので、僕にとってはこのほうがやりやすいが、そこまで気にしなくていいことなのかもしれません👀
この辺こうするといいとかあればぜひご意見お待ちしています🙏
以上です🌮
他にもグッドなプラクティスあればぜひ教えて下さい🙏
Discussion
コンポーネントがどのようにレイアウトされるか意識するべきではないという点で、この意見に賛成です。
ただ、例えば技術ブログ記事などで多用されるコード表現(?)などは、コンポーネントがinlineで使われることを知っていても自然なのかなと思います(しかし実装ももともとinline系の要素を使用すると思うので、意識する機会は少ないかもしれませんが。。)。
前提として、
ですが、加えて
として良いのかなと思いました。
コメントありがとうございます🙇♂️
code
などの文章に埋めこまれるパーツの場合はどっちかというと切り出さずにみたいな感じで、まるっとスタイルを当てたりすることになるかなーと思っています🤔
他で考えられるのはform系のパーツ(
input
,select
,textarea
,button
...)なんですが、これらもかならずblock
にすべきだろうかはちょっと悩むんですが、逆にblock
で困る機会は無い気もしてます🙄なので、
は僕も賛成ですが、exportされないコンポーネントに限るなど、絞ったほうが使いやすいかもしれません👀