今更聞けない!エンジニアのための CSS の基礎講座 〜ボックスモデル編〜

ヌーラボには現在フロントエンドエンジニアが僕を含め3名おり、CSS は主にそのフロントエンドエンジニアが書いていますが、細かい機能の追加や修正の際にはエンジニアも CSS を書くことがあります。

ただ、エンジニアの中には CSS はあまり得意でない方や、独学でなんとなくで書いている方も多く、以前、そんなエンジニアのために CSS の社内勉強会を行いました。そこで好評だった「CSS のボックスモデル」を抜粋して今回紹介したいと思います。

ボックスモデルは CSS の基本中の基本であり、これを理解していないと自分の表現したいレイアウトを実現できないくらい CSS を書く上で非常に重要です。

ボックスの生成

HTML の要素はどの要素も ボックス と呼ばれる四角形の領域を生成します。ボックスは大きく分けて ブロックボックスインラインボックス の2種類に分けられます。ブロックとインラインという言葉にピンときた方もいるのではないでしょうか?そうです、CSS の display プロパティ の値によってボックスの種類が決まります。

各HTML要素はブラウザのデフォルトスタイルシートによって display プロパティのデフォルト値が指定されているので、それに伴ってデフォルトのボックスの種類も決まっていますが、display プロパティを変更することでボックスの種類を変えることができます。

h1 要素の Google Chrome における display プロパティのデフォルト値は blockh1 要素の Google Chrome における display プロパティのデフォルト値は block

ではそれぞれのボックスにはどのような特徴があるのかを見ていきましょう。

ブロックボックスとは

display プロパティの値が block や table などの要素はブロックボックスを生成します。ブラウザのデフォルトでブロックボックスを生成する要素には h1~h6, p, div, ul, ol, li, table 要素などがあります。

ブロックボックスは幅に関係なく、上から下へ縦方向に配置されます。ブロックボックスの前後には改行が伴って、1つの段落としてレイアウトされると考えるとイメージがつきやすいかもしれません。

他にブロックボックスにはこのような特徴があります。

  • 幅(width)や高さ(height)を指定できる。幅を指定しなければ親要素の幅全体に広がる。
  • 上下左右の margin 、padding を指定できる

ブロックボックスのイメージ図ブロックボックスのイメージ図

インラインボックスとは

display プロパティの値が inline や inline-table などの要素はインラインボックスを生成します。ブラウザのデフォルトでインラインボックスを生成する要素には span,a, em, strong, img 要素などがあります。

インラインボックスは左から右へ横方向に配置されます。インラインボックスの前後には改行は伴わず、行内の一部としてレイアウトされると考えるとイメージがつきやすいかもしれません。

他にインラインボックスにはこのような特徴があります。

  • 幅(width)や高さ(height)を指定できない。※1
  • 上下の margin を指定できない。

インラインボックスのイメージ図インラインボックスのイメージ図

※1 インラインレベル要素でも img や input、 textarea 要素のように CSS で幅や高さを設定できる要素もあります。このような要素は置換要素と呼ばれ、幅や高さを指定できる HTML の属性(width, height, size, col など)を持っている特徴があります。

補足:インラインブロックボックス

display プロパティの値が inline-block の要素はインラインブロックボックスを生成します。インラインブロックボックスはインラインボックスと同じように横方向に配置されますが、ブロックボックスのように幅や高さ、上下の margin を指定できるので、横並びのナビゲーションなどのレイアウトに便利です。

display プロパティ

主要な display プロパティの値と生成されるボックスは以下のようになります。

display プロパティの値 ボックスの種類
block ブロックボックス
inline インラインボックス
inline-block インラインブロックボックス
list-item ブロックボックスとリストマーカ用インラインボックス
table, table-row, table-cell など table 構造を表現するボックス
none ボックスを生成しない

注意

HTML5 からは 見た目は CSS に任せ、HTML は文書の構造(セマンティック)を表すことのみに重点を置かれるようになりました。例えば箇条書きリストを作るのに div と display: list-item; を使って構築するより、箇条書きリストを表す ul li を使ったほうがよりセマンティックです。このように目的にあった HTML 要素を使うようにしてください。

ボックスモデルとは

CSS でレイアウトをする際には、 ボックスモデル を理解する必要があります。
CSS の定義するボックスモデルは以下の図のように 4つの領域で構成されています。

ボックスモデル解説図ボックスモデル解説図

content(コンテンツ)

テキストや画像など、要素そのものの内容が表示される領域です。この領域のサイズは width(幅) と height(高さ)プロパティで指定することができます。

padding(パディング)

content と border の間にある余白の領域です。要素の内側の余白を取るために使います。この領域のサイズは padding プロパティで指定することができます。

border(ボーダー)

padding の外側にある領域、いわゆる枠線です。この領域のサイズは border-width プロパティで指定することができます。

margin(マージン)

ボックスの一番外側の余白の領域です。この領域のサイズは margin プロパティで指定することができます。

ボックスサイズの算出

以下の例で、width: 100% の子要素(.box__inner)が親要素(.box)からはみ出てしまうのはなぜでしょう?(タブを切り替えることで HTML と CSS を確認できます。)

See the Pen WrbeLg by johnykei (@johnykei) on CodePen.

これは知っている方からすればすごく簡単なことなのですが、初心者の方はよくハマるポイントです。

ボックスのサイズは「content + padding + border + margin」の合計で算出されます。先ほど説明した通り、CSS の width と height プロパティは content の領域のサイズを指定する為に使うので、上記の例だと .box__inner の全体の幅は「500px(content) + 20px(padding) + 6px(border) = 526px」となります。

よって、親要素の 500px より大きくなりはみ出してしまっているわけです。でははみ出さないようにするにはどうすればいいのでしょうか?方法は以下の2つがあります。

方法その1:計算する

上を踏まえて、予め  padding + border + margin の幅を引いた値を width に指定すれば親要素に収まります。

.box__inner {
    width: 474px; /* 500px - 20px - 6px */
    padding: 10px;
    background-color: #fff4c3;
    border: 3px solid #ff8410;
}

このやり方は一昔前によくやっていました。

しかし毎回計算するのが面倒ですよね。そんな時は SASS 等の CSS プロセッサを使うか、CSS で計算を可能にする calc() を使うと計算する必要がなくなります。

.box__inner {
    width: calc(100% - 20px - 6px);
    padding: 10px;
    background-color: #fff4c3;
    border: 3px solid #ff8410;
}

calc() はすでに主なブラウザでは使うことができます。ベンダープレフィックスを付ける必要性もほとんどなくなってきています。
http://caniuse.com/#feat=calc

方法その2:box-sizing を使う

box-sizing プロパティを使うと CSS の width、height プロパティで指定できる領域のサイズを変更することができます。

box-sizing 解説図box-sizing 解説図

  • content-box: 初期値。width、height プロパティで指定できる領域は content 領域のみ
  • padding-box: width、height プロパティで指定できる領域に padding 領域を含める
  • border-box: width、height プロパティで指定できる領域に padding + border 領域を含める

よって上記の例だと box-sizing: border-box を使うだけで親要素からはみ出さなくなります。幅の計算は必要ありません。

.box__inner {
    width: 100%;
    padding: 10px;
    background-color: #fff4c3;
    border: 3px solid #ff8410;
    box-sizing: border-box;
}

box-sizing: border-box; を使えば CSS のレイアウトがとても簡単になります。sanitize.css などのリセット CSS を使うと、ルートの要素に box-sizing: border-box; を適用されるので、全ての要素において border-box が有効になり、個別に適用する必要がなくなります。

Bootstrap 等の CSS フレームワークでも box-sizing: border-box; を予め全要素に適用するのが最近では当たり前になってきています。このプロパティも主なブラウザでは使うことができますので、いちいち計算をしたりするよりもこちらの使用をおすすめします。
http://caniuse.com/#feat=css3-boxsizing

margin の相殺

先ほど説明したようにブロックボックスでは上下の margin を指定することができますが、縦に隣接したボックスの場合に上下の margin が重なることがあります。これは margin の相殺 と呼ばれています。意図せず margin の幅が狭くなってしまっている場合は大体これが影響しています。

margin の相殺は少し複雑で使いづらい印象がある方も多いとは思いますが、相殺を効果的に使って CSS をスリムに書ける場合もありますので、正しく理解しましょう。

margin の相殺が起きる時

margin の相殺は縦に隣接するボックスの上下の margin の間で起こります。また、margin の相殺はいつも起こるわけではなく、いくつか条件があります。

縦に隣接したボックスの場合

縦に隣接した同階層の要素の場合、上下の margin は大きい方の margin だけ適用されます。以下の例では、感覚的にはそれぞれの合計の 45px の margin になりそうですが、実際には相殺されて大きい方の .box2 の上 margin 30px だけが適用されます。

See the Pen gPbbzp by johnykei (@johnykei) on CodePen.

入れ子関係の場合

親要素とその先頭もしくは最後の子要素の上下 margin は、大きい方の margin だけ適用されます。文章だけでは少し難しいので以下の例を見てください。

See the Pen GoggXJ by johnykei (@johnykei) on CodePen.

この場合、親要素の .box2 の上 margin は 30px にはならず、その子要素の .box2-1 が持つ上 margin の 60px が適用されます。ただし、親要素に border や padding がある場合や、以下のように親要素と子要素の間にテキスト等の他の要素がある場合は適用されません。入れ子関係の margin の相殺は親要素と子要素が接している時だけ起こると覚えていただいても結構です。

See the Pen VeYYEJ by johnykei (@johnykei) on CodePen.

それ以外にもいくつか margin の相殺が起こらないケースがあります。その一部を以下にまとめました。

margin が相殺されない場合

全般

  • 水平方向(左右)の margin
  • ボックスに display: inline-block; が適用しているとき
  • ボックスに float を適用しているとき

入れ子関係

  • 親要素に padding, border を適用しているとき
  • 親要素に position: absolute, fixed を適用しているとき
  • 親要素に overflow: hidden, scroll を適用しているとき

それぞれの細かい解説は今回割愛します。

margin の相殺の利点

「なんで相殺ってあるの?」とか「難しいだけでメリットはないし邪魔なだけ。」って思われる方も多いと思います。確かに慣れるまでは意図せず margin の相殺がされてしまったりと少し難しいかもしれません。代わりに margin は top か bottom だけの単一方向しか使わないようにするとか、相殺のない padding を使うようにしたりなど対応策もあります。例えば Dropbox は スタイルガイド で margin-top は使わないようにしています。これは意図せぬ margin の相殺を避ける為のルールです。

しかし、先ほども言った通り margin の相殺を使わないとコードが複雑になってしまうケースもあります。例えばブログ記事用の HTML はその記事によって違うので、どの記事でも各要素毎の適切な余白を空けるのは単一方向の margin や padding だと難しいです。その場合は相殺が非常に有効です。このように相殺はイレギュラーに対応しやすく、Web アプリケーションにデザインを組み込む際にも非常に効果的ですので、エンジニアにこそ margin の相殺を使っていただきたいです。是非とも理解を深めて使ってくださいね。

margin と padding の使い分け

わりと悩ましい margin と padding の使い分けの例を僕なりにまとめてみました。当たり前のことばかりですが、良かったら参考にしてください。

margin を使う場合

  • border の外側に余白が必要なとき
  • 余白に背景色や背景画像を適用したくないとき
  • 値に auto を使いたいとき(中央寄せしたいときなど)
  • margin の相殺を利用したいとき
  • ネガティブマージン(マイナスの margin)を使いたいとき

padding を使う場合

  • border の内側に余白が必要なとき
  • 余白に背景色や背景画像が適用したいとき
  • 上下の隣接したボックスの余白にそれぞれの上下 margin の合計を適用したいとき(相殺されたくない場合)

まとめ

いかがでしたでしょうか?

今回書いた内容は本当に基本的なことですが、案外なんとなくで覚えている方も多いです。きちんと理解してなんとなくの CSS から卒業していただけると幸いです。


ヌーラボでは現在フロントエンドエンジニアは募集しておりませんが、CSS も自分で書きたい!というエンジニアも大歓迎です。是非こちらからご応募ください。

より良いチームワークを生み出す

チームの創造力を高めるコラボレーションツール

製品をみる