CSSのmarginを避けるべき理由
マージンは事実上の副作用です。スコープ外の要素の配置に影響を与えるため、カプセル化を破壊します。したがって、可能な限り避けるべきです。
要素にマージンを使ってページ内の要素をレイアウトする代わりに、親要素がページのレイアウトとスペーシングを定義すべきです。FlexboxやGridのgapを使うか、> * + *で子要素のマージンを定義することで実現できます(ただし、これはFlexboxとgapを使うよりも好ましくないと思います。多くの場合、最後の子要素にマージンを付けたくないため、> *:not(:last-child)のようにさらに具体的に指定する必要があり、gapを定義するよりもはるかに複雑になります)。
マージンは、適用される要素が特定のコンテキストでのみ使用される場合(異なるコンテキストで再利用できない場合)にのみ使うべきです。マージンはスコープ外の要素に影響を与えるため、マージンが存在する場所ではその使用コンテキストを理解する必要があり、コードを読む開発者の認知負荷が増大するため面倒です。
一方で、これはマージンの使用が有効なのは、再利用性が特定のコンテキストに限定される場合のみ、つまり複数のサブコンポーネントで構成されるコンポーネントの場合であることを意味します。例として以下のCardコンポーネントを見てみましょう:
function Card({children}) {
return <div>{children}</div>
}
function CardHeader({children}) {
return <div className="m-2">{children}</div>
}
function CardContent({children}) {
return <div className="m-3">{children}</div>
}
function CardFooter({children}) {
return <div className="m-4">{children}</div>
}このように使います:
<Card>
<CardHeader>
Card header
</CardHeader>
<CardContent>
Card content
</CardContent>
<CardFooter>
Card footer
</CardFooter>
</Card> Cardを構成するパーツ間のスペーシングが異なる場合、コンテナ(Card)に1つのスペーシング値だけを設定することはできません。さらに、CardコンポーネントのサブコンポーネントはCardの子要素としてのみ使われるべきなので、ここでのマージンの使用は有効です。
しかし、このシナリオでも、代わりにpaddingを使うことでマージンを避けることができます。唯一の潜在的な欠点は、paddingは要素内部のスペースですが、要素間のスペースを定義するために使っているため、メンタルモデルとして少し違和感があるかもしれないことです。とはいえ、マージンをどこかで使うこと自体も、すでに述べた理由で欠点と見なせるため、このコンテキストではpaddingとmarginの使用は同等に有効だと考えています。
function Card({children}) {
return <div>{children}</div>
}
function CardHeader({children}) {
return <div className="p-2">{children}</div>
}
function CardContent({children}) {
return <div className="p-3">{children}</div>
}
function CardFooter({children}) {
return <div className="p-4">{children}</div>
}このように、少数のユースケースを除いてマージンを避けることで、副作用のないより良いコンポーネントを構築することが促され、結果としてより再利用可能で、読みやすく、メンテナンスしやすいコードになります。