CSS でレイアウトを作る時に考えていること(margin/width/height)

この記事は、アソビュー! Advent Calendar 2022 の 17日目(裏面)です。

みなさん、こんにちは。アソビュー!でフロントエンドエンジニアをしている白井です。

弊社ではバックエンドエンジニアの比率が多いこともあり、CSS 怖い!CSS わからない!という声をよく聞きます。

CSS と少しでも仲良くなるために、今回はレイアウトを作る際に基本となる幅(width)、高さ(height)、余白(margin)を指定する際に考えていることなどお話ししようと思います。

まずはじめに

レイアウトを整える前に、不要な CSS プロパティは消しておく

普段作業していて CSS を書き換えていると不必要なスタイル定義は意外と残ってしまいがちです(よくやってしまいます…)。

色や形に関するプロパティは大体決まっているので重複したりすることは少ないですが、要素の位置は displaypositionmarginwidthheight …など、様々なプロパティの組み合わせによって決定されるため、余計なものはなるべく削除しておいた方が見通しが良くなります。

たとえば、

  • 要素のデフォルトスタイルを適用している
  • 既に適用済みのスタイルを再適用している(よくある)
  • オーバーライドしているが、優先度の関係で適用されていない

これらはコード上で分かるものは削除し、わからない場合は Chrome DevTool などで確認して削除・修正しています。

<div class="list">
  <span class="item">...</span>
</div>

<style>
  .list {
    margin: 0; // ①
    width: 100%; // ②
  }
  .list .item {
    color: yellow; // ③
  }
  .item {
    display: inline; // ④
    color: cyan !important;
  }
</style>
  • ① 実は全ページで読み込んでいる reset CSS で margin: 0 を適用済み
  • div はブロック要素のため、もともと width100%
  • color をオーバーライドしているが、優先度で負けているため適用されない
  • span はインライン要素のため、もともと displayinline

適用する要素のタグが不特定(block にも inline にもなる可能性がある)場合に display を固定化する意味で設定したり、flex-item 要素になったときには width: 100% ではなくなるためあえて 100% の指定が必要など、シーンに応じて必要・不必要は考えなくてはならないですが…

width の指定

昨今ではレスポンシブ対応をすることがほとんどだと思うので、常に画面幅が増減したときのことを考える必要があります。

width → max-width に変える

width の指定をすると、画面幅が縮んだときにコンテンツが収まりきらなくなる可能性が出てくるので、幅が変化する方が困るという時以外はなるべく指定しないようにしています。

代わりに、max-width を使って最大幅だけ決めておき、その幅以下になった場合に可変するようにしておく(width100% になっていること前提)と画面幅が縮んだときのことを考えなくて済みます。

例:800px のコンテナの幅指定

PC では 800px、それ以下では親要素に依存した 100% になっているような想定です。こういったコンテナは max-width にしておくだけでレスポンシブに対応が楽になります。

.container {
  max-width: 800px;
  margin-right: auto;
  margin-left: auto;
}

実際の動きを見てみたい方はこちら ▶︎

子要素の width は grid-template を使う

要素を横並びして、均等の幅で子要素を並べるようなシーンで width を指定することはよくあると思いますが、親の幅、子要素の数、余白の値からどれくらいの値を設定するか計算する必要があります。

そこで、子要素に width は持たせず、親要素を grid にして子要素の幅を grid-template で設定することで width を自動計算させることができます。

例:子要素を3列、均等幅で配置にするときの指定

親要素を grid にして列幅を指定、さらに均等幅で配置する場合は要素の数で自動計算してくれる 1fr の単位を使います。これでよりフレキシブルになります。

<section class="cards">
  <div class="card">
  <div class="card">
  <div class="card">
</section>

<style>
  .cards {
    display: grid;
    grid-template: auto / repeat(3, 1fr);
    max-width: 800px;
    margin: 0 auto;
    gap: 10px;
  }
  .card { ... }
</style>

実際の動きを見てみたい方はこちら ▶︎

grid-template行の高さ・数 / 列の幅・数 の設定を行えます。ここでは、

  • :1行、高さは auto のため中身によって可変します。
  • :3列( repeat(3, 1fr)1fr 1fr 1fr の意)、長さは全て 1fr で3列の要素が均等の幅で配置されます。

例2:子要素を3列、列幅を指定して段落ちできるようにする

幅の指定が必要になりますが、auto-fit と組み合わせると flex 要素のように収まりきらない要素を段落ちさせることもできます。 ここでは minmax() を使い min の値を auto とすることで、親要素が 200px を下回ったときに親要素に合わせて縮小させることができます。

  .cards {
    display: grid;
    grid-template: auto / repeat(auto-fit, minmax(auto, 200px));
    justify-content: center;
    margin: 0 auto;
    gap: 10px;
  }

実際の動きを見てみたい方はこちら ▶︎

height の指定

height は画像の大きさ、テキスト量などによって変化することが多いため、height で高さを固定化してしまうと、はみ出たり崩れてしまう可能性があります。動的ではなく静的コンテンツでも、レスポンシブで画面幅が増減した時にテキストの改行によって高さが変化した時のことを考えなくてはなりません。

padding を使う

height はその名の通り高さを設定するプロパティですが「見た目を整えるためにとりあえず高さがほしい」という理由では設定せず、高さが欲しい場合はまず padding で調整するようにしています。

コンテンツに応じて高さも増えるのではみ出したりすることなく、またほどよい余白をあけるという意図もあります。

min-height を使う

height を使いたくなった場合、最小の高さを保持してそれ以上になったときに可変してくれる min-height で整えると変化に強くなります。

基本的には height は設定せずに高さは中身のコンテンツに任せて padding で調整、それでも高さの設定が必要な場合に min-height、それでも難しい場合は height を設定する くらいの気持ちで考えるようにしています。

例:ボタンコンポーネント

height を使わずに paddingmin-height で高さ調整した例です。

(ここまでウィンドウが縮まることは無いと思いますが、2列で配置された時などを想定)

.button {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 30px;
  padding: 10px;
  background-color: cadetblue;
  color: white;
  box-sizing: border-box;
}

実際の動きを見てみたい方はこちら ▶︎

  • padding の設定
    • 高さを調整する前にまず padding を入れます。font-size が変化するような場合、em の単位を使うと文字サイズに対応した余白をあけることができます。
  • min-height の設定
    • padding の調整である程度の高さは取られているはずですが、中途半端な値になったり、最小の高さをもっと取っておきたいことも。デザイン上、きっちりした高さを設定したい場合は padding で調整しつつも「最低この高さは取りたい」というのを min-height で調整します。

padding で高さの調整を行い、height ではなく min-height とすることで、ボタンの中の文字が二行になった場合でもデザインを保つことができます。

margin の指定

margin は要素のコンテナの外側の余白を取るときに使用するプロパティです。

余白を取るために使うのは正しいのですが、margin で余白を取っていくと、どの要素によって余白がとられているのかが分かりにくくなったり、margin の相殺などを考えて各要素に設定されているプロパティをみて余白の計算を行う必要がでてきます。

要素間の余白は margin → gap に変える

要素間の余白を取る際、margin の代わりに、親要素に display: flexdisplay: grid を設定し、子要素間の余白を一括設定できる gap プロパティを使うと、親要素側で余白をコントロールできるようになり、管理しやすくなります。

例:各要素間に 10px ずつ余白をあける

<section class="list">
  <div>...<div>
  <div>...<div>
  <div>...<div>
</section>

<style>
  .list {
    ...
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
</style>

実際の動きを見てみたい方はこちら ▶︎

これにより、子要素の記述量が減るため場合によってはクラスを付与する必要がなくなるというメリットもあります。

注意点として、iOS の flexgap プロパティは 2021年4月の safari 14.1 から対応したため、バージョンの利用率などを見ながら使うかどうか判断するのがよさそうです。

一方で gridgap は iOS は 2018年ごろ から対応しています。

ページは要素と要素の積み重ねで出来上がっていくので、その余白を gap で取るようにすると margin の出番はそこまで多くありません。

まとめ

よく使われる widthheightmargin プロパティですが、意外と使うシーンは少なかったりします。もちろん、組み方は色々あるので今回紹介した内容以外にも方法はありますが、なるべく記述量を減らし崩れにくいスタイリングをしていきたいですね。

レイアウトのプロパティといえば、他に displayposition も外せないところですが、話が長くなりそうなのでまた別の機会に。

CSS プロパティも新しいものが増えてどんどん便利になっていくので、対応ブラウザ・シェア率と相談しながら徐々に使っていきましょう!

アソビュー!では一緒にはたらくメンバーを募集しています。 ご興味のある方いらっしゃいましたら、ぜひ一度お話ししましょう!

www.asoview.com