Tips

CSSでレイアウトが崩れる人向け!ボックスモデルを理解すればOK【width, height, margin, padding, border, box-sizing, gap】

  • このエントリーをはてなブックマークに追加

「デザインデータ通りのパーフェクトピクセルで作れたことがない」
「コーディングでレイアウト崩れが多く修正ばかりしている気がする」
「CSSの余白をつけるプロパティでmargin, padding, gapの違いがよくわからない」

本日はそんなコーディング初心者の方に向けた記事になっています。

もしCSSでレイアウトが崩れてしまうことに悩んでいるのであれば一読いただくと良いかもしれません。

今回はCSSの基本的な概念である「ボックスモデル」を理解する方法を解説します。

幅(width)、高さ(height)、マージン(margin)、パディング(padding)、ボーダー(border)、ボックスサイジング(box-sizing)、ギャップ(gap)など、ボックスモデルに関連する要素について詳しく説明し、それらがどのように連動するかを知っていただきます。

これらの基本的な概念を理解するだけで、CSSの文法やレスポンシブ対応などの理解が進むはずです。

また動画もあるので必要な方は以下からご覧ください。

CSSのボックスモデルを理解するだけでレイアウト崩れは格段に減る

スクールや学習教材などで必ずCSSのボックスモデルが登場しますが、手を動かして練習するというより座学のように習う方が多いはずです。

そのため興味を持ちづらいですし習っても忘れてしまうことになります。

しかし実際にはCSSのボックスモデルを理解していないとデザインカンプをもとにしたコーディングは一生上達しません。

とはいえ数学的な知識は不要ですし誰でも理解できるものなので今一度やってみましょう。

HTML要素は四角形から始まる

まず最初に知って欲しいことは「HTMLで書かれた要素は全て四角形である」ということです。

例えば以下のようなHTMLがあります、<h1>と<p>が四角形であるという認識がない方が一定数います。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="./style.css" />
    <script src="./script.js" defer></script>
  </head>
  <body>
    <h1>タイトル</h1>
    <p>テキストです。</p>
  </body>
</html>

確かにブラウザで表示すると四角形というより文字なので仕方ありません。

わかりやすくするためにCSSを追加してみます。

背景色をつけると理解しやすいはずです。

h1 {
  background: red;
}
p {
  background: blue;
}

こういう見た目だと先ほどの「四角形」という意味がわかるはずです。

ボックスモデルという言葉にもあるように、基本的にHTML要素というのは四角形で存在しています。

そこからCSSによって円形や線になったりするわけですが最初の形は四角形です。

縦横のサイズはいくらになる?

続いてCSSでサイズを指定する方法を解説していきます。

「それくらいは分かっている」という方がいるかもしれません、しかし何となく理解だけではデザインデータ通りのレイアウトが作れなかったりしますので復習しましょう。

先ほどのHTMLを使ってCSSを書いていきます。

h1 {
  background: red;
  /* ここを追加 */
  padding: 10px;
  border: 5px solid #333;
  width: 300px;
  height: 300px;
}
p {
  background: blue;
}

<h1>に基本的な縦横のサイズと枠線、余白をつけてみました。

それでは<h1>の縦と横のサイズは何pxでしょう?

「縦が300pxで横が300px」と思った方、残念ながら違います。

検証ツールを確認してみましょう。

今のCSSの設定だと「縦が330pxで横が330px」というのが正解です。

これはどういう計算かというと、

縦のサイズ= height + padding-top + padding-bottom + border-top + border-bottom
横のサイズ= width + padding-left + padding-right + border-left + border-right

という計算です。

これがボックスモデルの基本になっていて、widthとheightに加えてpaddingとborderの値もサイズ計算に含められるようになっています。

ちなみにmarginは外余白になるので計算には含めません。

「widthとheightの値だけ見ておけば良い」と思っていた方はデザインデータ通りに数字を指定しても微妙にズレているかもしれませんね。

もし<h1>を縦横それぞれ300pxにしたい場合は以下のようにする必要があります。

h1 {
  background: red;
  padding: 10px;
  border: 5px solid #333;
 /* ここを修正 */
  width: 270px;
  height: 270px;
}
p {
  background: blue;
}

縦横のサイズにpaddingとborderの値が含まれるということで、事前にwidthとheightの値から差し引いておけば良いわけです。

これで300pxにすることができました、算数の足し算と引き算なので慣れれば簡単です。

しかしなかには「計算するの面倒だな」と思われた方がいるかもしれません。

実はCSSでは今やったようなpaddingとborderの値を事前に差し引くことを自動でやってくれるプロパティがあります。

h1 {
  background: red;
  padding: 10px;
  border: 5px solid #333;
 /* もう一度300pxに戻す */
  width: 300px;
  height: 300px;
  /* ここを追加 */
  box-sizing: border-box;
}
p {
  background: blue;
}

box-sizing: border-box;というものを追加しておくと、事前にpaddingとborderの値を引いて縦横のサイズを自動計算してくれます。

これでwidthとheightを300pxに戻しても同じ見た目になります。

検証ツールの右下にあるレイアウト図の方を確認してみると、widthとheightが300pxに戻したはずなのに270pxに変更されていることがわかります。

最近ではbox-sizing: border-box;を追加することが当たり前になっていますので、皆さんもクセづけてください。

ちなみに毎回box-sizing: border-box;を書かなくてもCSSの先頭でユニバーサルセレクタのなかで指定すれば1回書くだけでOKになります。

/* ここを追加 */
* {
  box-sizing: border-box;
}
h1 {
  background: red;
  padding: 10px;
  border: 5px solid #333;
  width: 300px;
  height: 300px;
}
p {
  background: blue;
}

marginとpaddingのリセット

「*」とセレクタに書くことで「全てのタグ」という意味になります。

流れで一緒に説明しておくとmarginとpaddingもユニバーサルセレクタに書くことが多いです。

/* ここを修正 */
* {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}
h1 {
  background: red;
  padding: 10px;
  border: 5px solid #333;
  width: 300px;
  height: 300px;
}
p {
  background: blue;
}

他人のサンプルコードで見たことがあるかもしれません、わざわざ「0px」にする意味を知っていますか?

最初の画面をもう一度出します。

続いて現在の画面はこちらです。

赤と青の間にある余白があるのと無いのと違ってますね、実はHTML要素はデフォルトでpadding, marginが当たっています。

そのため一度リセットするためにもユニバーサルセレクタで「0px」としていたのです。

こちらもデザインデータを元にしたコーディングでズレが起きる原因の一つです。

もちろんリセットは必須では無いですが制作会社やエンジニアの方の動向を見ていると、リセットする派の方が多い印象です。

マニアックなことを言うとHTMLタグの種類によってはデフォルトで追加されているCSSのプロパティと値がバラバラです。

例えば<button>ではbackgronud-colorとborderがデフォルトで追加されています。

デフォルトで追加されるものはmarginとpadding以外にも無数にあるわけです。

そのため実務を想定するとリセットCSSのような専用のCSSファイルを追加で読み込ませたりします。

本記事では触れませんが興味がある方は「リセットCSS」で調べてみてくださいね。

共通CSSは*(ユニバーサルセレクタ)、html、bodyのどれに書く?

スクールや学習教材で「共通で使うプロパティはここに書きましょう」という具合に、*(ユニバーサルセレクタ)、html、bodyの3つのセレクタを紹介されたはずです。

初心者の方のなかで「それぞれ3つの違いはあるの?」と思われた方がいるかもしれません。

実は違いを知っておかないとハマる可能性があるので一緒に解説しておきます。

htmlとbodyは必ずしも画面いっぱいとは限らない

よく使うプロパティにbackgroundを使った背景指定があります。

backgroundは*(ユニバーサルセレクタ)、html、bodyのどこで書いても画面いっぱいになります。

そのため「html,bodyタグは自動的に画面いっぱいになる」という理解をしている方がいますが微妙に違います。

例えば以下のようなマークアップをした場合に検証ツールを確認してみます。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="./style.css" />
    <script src="./script.js" defer></script>
  </head>
  <body>
    テストです
    <h1>タイトル</h1>
    <p>テキスト</p>
  </body>
</html>
* {
}
html {
}
body {
  background: red;
}

bodyに背景色を指定すると画面いっぱいに背景色がつきましたが、検証ツールでbodyタグを確認すると高さが画面サイズより小さいですよね。

htmlタグとbodyタグの高さは「コンテンツの分だけ」というのがセオリーになっています。

しかし背景色は画面いっぱいに広がっていますよね、こちらはブラウザの仕様になっていてheight: 100vh;を書かなくても自動的に画面いっぱいにしてくれているのです。

だからこそ誤解しやすいですよね。

*(ユニバーサルセレクタ)はhtmlタグの親要素ではない

ちなみにhtmlタグが最上位タグになっていて*(ユニバーサルセレクタ)はhtmlタグの親要素ではないのも説明されないことが多いみたいです。

*(ユニバーサルセレクタ)はHTMLタグではなく「ここに書いたものは自動的に全ての要素に反映しますよ」という特殊なセレクタです。

そのため余白のリセットとbox-sizingの指定は*(ユニバーサルセレクタ)に書くことになります。

例えば余白のリセットをbodyタグに書いてしまうと上手くいきません。

* {
}
html {
}
body {
  background: red;
 /* ここを追加 */
  margin: 0;
  padding: 0;
}

h1タグのデフォルトのmarginが残ったままですね。

一方でbodyタグに余白のリセットを書いているのでbodyタグ自体にはデフォルトのmargin,paddingは無くなっています。

こちらの現象はhtmlタグでも同じ結果になります。

何気なく書かれているサンプルコードを見てコピペするだけになっていると自分で書いたときにハマることになりますので注意しましょう。

しかしここまで読んできた流れで「じゃあbackgroundも*(ユニバーサルセレクタ)に書いた方が良いの?」という発想になった方がいるはずです。

*(ユニバーサルセレクタ)に書く共通設定は余白とbox-sizingだけにしてください。

backgroundまで書いてしまうと少々面倒なことになります。

HTMLファイルに要素を追加して検証します。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <!-- <link rel="stylesheet" href="./style.css" /> -->
    <link rel="stylesheet" href="./main.css" />
    <script src="./script.js" defer></script>
  </head>
  <body>
    テストです
    <h1>タイトル</h1>
    <p>テキスト</p>
    <!-- ここから追加 -->
    <div><p>入れ子です</p></div>
    <div>入れ子じゃないです</div>
  </body>
</html>

続いてCSSは以下のように書きます、背景色はdivタグと*(ユニバーサルセレクタ)に別の色で指定しておきます。

* {
  margin: 0;
  padding: 0;
  background: blue;
}
html {
}
body {
}
div {
  background: red;
}

divタグに背景色を赤色と指定していたはずですが、2つあるdivタグのうち1つは青色のままです。

よく見てみると*(ユニバーサルセレクタ)の背景色が子要素であるpタグに適用されていることが原因だとわかります。

*(ユニバーサルセレクタ)は「全ての要素」に対して値を指定できるので、逆にいうとbackgroundを指定していない要素には問答無用で*(ユニバーサルセレクタ)で指定したものが適用されます。

一方でhtml,bodyタグは「子要素に継承」という仕組みがあるため、今回のように子要素にbackgroundを指定していない場合は自動的に親要素のbackgroundにしてくれます。

CSSを以下のように変更します。

* {
  margin: 0;
  padding: 0;
  /* 背景を削除 */
}
html {
}
body {
  /* ここを追加 */
  background: blue;
}
div {
  background: red;
}

今度は2つあるdivタグの両方に背景色を指定することができています、というよりdivタグの子要素であるpタグには背景を指定していませんが親要素であるdivタグの指定を継承させたわけです。

冷静に考えたらそんなに難しい話ではないのですが、*(ユニバーサルセレクタ)とhtml,bodyタグの違いを知っていないと迷うかもしれません。

先ほども言いましたが*(ユニバーサルセレクタ)はhtmlタグの親要素ではありません。

そのため継承させる仕組みがなく、すべての要素に自動的に指定することになります。

便利な機能ですが今回のようなケースでは困りますよね。

backgroundはhtml, bodyタグに書くようにしましょう。

CSSにおけるmarginとgapの違いは?

marginの話からgapというプロパティについてもお話しておきます。

marginはご存知の通り外側の余白を設定するものですが、それとよく似たプロパティにgapがあります。

gapは最近になって登場したもので知らない方がいるかもしれませんが、ここ数年で使う人が増えていますので初心者の方もぜひ1回は練習しておくことをお勧めします。

例題として以下のようなHTMLをもとに横並びのレイアウトを作ってみます。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="./style.css" />
    <script src="./script.js" defer></script>
  </head>
  <body>
    <div class="cards">
      <div class="card">
        <h1>01</h1>
        <h2>Web制作</h2>
        <p>
          この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、
        </p>
      </div>
      <div class="card">
        <h1>02</h1>
        <h2>マーケティング</h2>
        <p>
          この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、
        </p>
      </div>
      <div class="card">
        <h1>03</h1>
        <h2>動画編集</h2>
        <p>
          この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、
        </p>
      </div>
      <div class="card">
        <h1>04</h1>
        <h2>Webライティング</h2>
        <p>
          この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、
        </p>
      </div>
      <div class="card">
        <h1>05</h1>
        <h2>SEOコンサル</h2>
        <p>
          この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、
        </p>
      </div>
      <div class="card">
        <h1>06</h1>
        <h2>デザイン</h2>
        <p>
          この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、
        </p>
      </div>
    </div>
  </body>
</html>

こちらを3列3行のレイアウトにする場合、以下のようなCSSが一般的だと思います。

皆さんも一度は作ったことがあるはずです。

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  margin: 16px;
  background: #eee;
}
.cards {
  width: 1000px;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
}
.card {
  width: calc((1000px - 32px) / 3);
  padding: 56px 40px;
  background: #fff;
  box-shadow: 0 16px 32px rgba(0, 0, 0, 0.1);
  margin-right: 16px;
  margin-bottom: 16px;
}
.card:nth-child(3n) {
  margin-right: 0;
}
.card:nth-child(n + 4) {
  margin-bottom: 0;
}

カード1枚あたりのwidthを設定して、横と縦の余白をmarginで設定しています。

横並び自体はdisplay: flex;を利用したものです。

こちらと全く同じレイアウトをgapを使うと以下のようになります。

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  margin: 16px;
  background: #eee;
}
.cards {
  width: 1000px;
  display: flex;
  align-items: center;
  /* ここに書く */
  gap: 16px;
  flex-wrap: wrap;
}
.card {
  width: calc((1000px - 32px) / 3);
  padding: 56px 40px;
  background: #fff;
  box-shadow: 0 16px 32px rgba(0, 0, 0, 0.1);
}

だいぶとスッキリしたコードになりました。

gapは今回のような横並びレイアウトにおける要素の間をどれくらい空けるかを指定するものです。

正確には縦の間隔は「row-gap」、横の間隔は「column-gap」というプロパティです。

上記のコードはmargin, paddingでやるような省略した書き方をしており正確には以下のような書き方になります。

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  margin: 16px;
  background: #eee;
}
.cards {
  width: 1000px;
  display: flex;
  align-items: center;
  /* 縦の間 */
  row-gap: 16px;
  /* 横の間 */
  column-gap: 8px;
  flex-wrap: wrap;
}
.card {
  width: calc((1000px - 32px) / 3);
  padding: 56px 40px;
  background: #fff;
  box-shadow: 0 16px 32px rgba(0, 0, 0, 0.1);
}

gapのパターンも検証ツールを確認しておきましょう。

同じレイアウトですがカード単体に焦点を当てて見てみると違いがあります。

先ほどと違ってmarginには何も値が入ってませんね、もちろんmarginをgapに書き換えたからです。

初心者の方だとパッと見てmarginとgapが同じものだと誤解されるのですが実際には違うものです。

そのため別のレイアウトを作っているときにmarginの代わりにgapを使っても意図した見た目にならないことになります。

marginとgapの勘違いによるレイアウト崩れも初心者のうちはあるので注意しましょう。

また今回参考にした本はこちらになります。

良ければこちらからどうぞ。

  • このエントリーをはてなブックマークに追加