コンポーネントを使いこなすmargin(間隔)の考え方

ReactやVueがフロントエンドで多く使われるようになりコンポーネントを意識した実装が求められるようになりました。
HTML、CSSのみのマークアップにも言えることですがコンポーネントを意識して実装すると考えなければいけないことがあります。
それはコンポーネントごとのmargin の適用です。
marginは一体どこにつければ良いのか?、普段から意識しておかないとメンテナンス性を残ったり変更に弱い実装をやってしまいかねません。
どうすればメンテナンス性を考慮した変更に強い実装ができるのか方法ごとに使い所をしていきます。
方法1:コンポーネントを囲ったタグにmarginを適用
まずはコンポーネントをタグで囲ってmarginを適用する方法です。
<div> <h1>ドリンク</h1> <div class="componentWrapper"> <div class="drink">コーラ</div> </div> <div class="componentWrapper"> <div class="drink">カルピス</div> </div> <div class="componentWrapper"> <div class="drink">ファンタ</div> </div></div>
.componentWrapper { margin-bottom: 20px;}
.componentWrapper:last-child { margin-bottom: 0;}
使い所
この方法のメリットはコンポーネントごとに柔軟にmarginを適用できることです。
上記のサンプルのようにmarginを適用したいコンポーネントが同じであれば、コンポーネントごとにmarginを適用したいケースは少ないかもしれません。
ですが、コンポーネントが違えばmarginの数値も変更になることは多くあります。
例えば下記のようなdrink、category、snsのように性質の異なるコンポーネントが並ぶ場合です。
<div> <div class="drinkWrapper"> <div class="drink">コーラ</div> </div> <div class="categoryWrapper"> <div class="category">カテゴリー:炭酸</div> </div> <div class="snsWrapper"> <div class="sns">SNSでシェア</div> </div></div>
このような場合でそれぞれ違うmarginを適用する場合はコンポーネントごとにタグで囲ったmarginの適用のメリットが受けられます。
方法2:コンポーネント囲ったタグにgapを適用
コンポーネントごとにタグは囲まず、marginを取りたいコンポーネントをまとめて1つのタグで囲む方法を見ていきます。
<div class="components"> <div>Component</div> <div>Component</div> <div>Component</div></div>
.components { display: flex; flex-direction: column; gap: 20px;}
使い所
この方法のメリットはコンポーネントごとにタグで囲むことなく、一貫して均等なmargin適用を強制できる点です。
方法1のようにコンポーネントごとにタグで囲ったmargin適用をすると運用する過程で一部だけ別のスタイルがついてmarginが変わってしまうという可能性もありません。
その反面、同じmarginの値しか適用できないため方法1で紹介した違うコンポーネントを並べるような場合には向きません。
そのためこの方法を採用する場合は同じコンポーネントを並べるとき、と覚えておくとよいと思います。
例えばカード型のUIで縦方向だけではなくグリッドでコンポーネントを並べるような場合にも相性が良いです。 その場合は下記のようCSSを適用すると横に3列並べることも可能です。
.components { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px;}
補足:ReactやVueを使用している場合
もしReactやVueを使っていて同じコンポーネントを並べる場合はcomponentsを1つのコンポーネントとして、mapでコンポーネントごとにタグで囲む方法でも同じようなことが可能です。
ReactやVueを使っている場合は囲ったdivにkeyをつけることができるので、この方法をよく使います。
<div className="components"> {[1, 2, 3].map((i) => { return ( // サンプルのためmapのindexをkeyに指定しています <div className="components__item" key={i}> <div class="component">Component</div> </div> ) })}</div>
.components__item { margin-bottom: 20px;}
.components__item:last-child { margin-bottom: 0;}
コンポーネントにmarginを適用するのはNG
今回の記事では2つのmagin(間隔)の取り方を見てきましたが、そもそもコンポーネントにmarginをつけてしまってはいけないのか。
例えば方法1のような場合コンポーネントをタグで囲まなければコードの記述量は減ります。
<div> <h1>ドリンク</h1> <div class="drink">コーラ</div> <div class="drink">カルピス</div> <div class="drink">ファンタ</div></div>
.drink { margin-bottom: 20px;}
.drink:last-child { margin-bottom: 0px;}
メンテナンス性の低下を招く
上記のHTMLとCSSでも方法1と同じ見た目になりますが、この方法はメンテナンス性を大きく損ないます。
なぜならdrinkコンポーネントが他の場所で使われる可能性があるからです。
このようなHTMLになった場合はどうでしょうか。
<div> <div class="drink">コーラ</div> <p>文章が入る</p></div>
drinkにmarginを適用していることによりここではdrinkの下に40px開けたいと思っても20pxが適用されます。
では上書きすればいいのでは?となると地獄の始まり。
<div class="drinkContainer"> <div class="drink">コーラ</div> <p>文章が入る</p></div>
.drinkContainer .drink { margin-bottom: 40px;}
これで意図した通り40pxになりますが、上書きはさらに上書きを呼びます。 特に複数メンバーで運用していCSSの習熟度もバラバラのような場合にどこのスタイルが有効になっているかわからず、時間に追われて誰か一人が!importantを使って上書きするなんてこともざらに起こります。
こうなってしまってはもうメンテナブルに運用するのは不可能になります。
コンポーネントをタグで囲むときのポイント
コンポーネントをタグで囲ってmarginを適用することでメンテナブルにできますが、より良くするための考え方があります。
コンポーネントを囲うタグには汎用性は必要ないということです。
汎用性を持たせてどこでも使えるようにしたほうが便利かもしれませんが、メンテナンス性を上げるにはあえて汎用性を落とすことも必要な場合があります。
汎用性を上げるということは複数箇所で使われることを想定しています。
ですが複数箇所で使われるということはそれだけ他の箇所で定義されるスタイルに影響を受けやすいということです。
極端な例で言うとトップページで方法1のHTMLを作るときにtopPageDrinkItemのようなそのページだけでしか使われないクラス名を使います。 このようにしておくと、トップページ以外から影響を受けることなく与えることもなくなります。 (topPageDrinkItemというクラス名が他でコピペされてしまうとどうしようもありませんが)
<div> <h1>ドリンク</h1> <div class="topPageDrinkItem"> <div class="drink">コーラ</div> </div> <div class="topPageDrinkItem"> <div class="drink">カルピス</div> </div> <div class="topPageDrinkItem"> <div class="drink">ファンタ</div> </div></div>
もしくはbodyに一意のクラス名がついているのであれば、そのクラスからスタイルを定義するのも検討材料の1つになりえます。
.topPage .drink { margin-bottom: 20px;}
ただしスタイルの詳細度を上げてしまうとやはり上書きの可能性が出てきしまうので、やらなくても良いのであればクラス単体でmarginを適用したほうが基本的には良いです。
まとめ
HTMLとCSSの設計の観点から今回はmarginについて考え方を書いてみました。
margin1つとっても結構奥深いHTMLとCSSの設計ですが、ReactやVueからフロントエンドを始められた方やHTMLとCSSはわかるけどあまり得意ではないという方に響くと嬉しいです。
コードコネクトではフロントエンドアプリケーションの設計を得意としていますので、お力になれることがありましたお気軽にご相談ください!
https://codeconnect.co.jp/services/development/