CSSのpaddingとmarginを%で書くと上下も親の幅で決まる

​​CSS​CSS Grid,Flexbox,Playwright

CSS の padding と margin をパーセントで書くと、左右だけでなく上下の値も、親(包含ブロック)の幅を基準に計算されます。親の高さは関係ありません。余白を画面の幅に合わせて伸び縮みさせたいときや、画像やカードの縦横比を保つ箱を作るときにはこの性質が使えますが、縦の中央寄せのつもりで margin-top: 50% と書くと、思った位置には来ません。

補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、ヘッドレスの Chromium 153 です。幅 800px・高さ 600px の画面で、body の margin を 0 にし、getComputedStyle() で計算後の padding と margin を、getBoundingClientRect() で箱の位置と大きさを測りました。Firefox と Safari、スマートフォンのブラウザは試していません。

上下の padding も、親の幅の何パーセントかで決まる

幅 400px・高さ 100px の親の中に .box を置き、padding: 10% を書きました。計算後の padding は、上・右・下・左のすべてが 40px でした。親の幅 400px の10%です。高さ 100px の10%(10px)にはなりません。

css
.parent { width: 400px; height: 100px; }
.box { padding: 10%; }

次に、.box に padding-top: 10% だけを書き、親の幅と高さを片方ずつ変えて測りました。

親の大きさ .box の padding-top
幅 200px・高さ 100px 20px
幅 400px・高さ 100px 40px
幅 800px・高さ 100px 80px
幅 400px・高さ 50px 40px
幅 400px・高さ 300px 40px

親の幅を変えると padding-top も同じ割合で変わり、高さを変えても 40px のままでした。

3つの親の箱を縦に並べた画像。幅300px・高さ120pxの親では、上の余白(水色)が細い。幅600px・高さ120pxの親では、上の余白が倍の太さになっている。幅300px・高さ240pxの親では、親が縦に長くなっても上の余白は一つ目と同じ細さのまま
padding-top: 10% を書いた箱を、大きさの違う3つの親に入れたところ。水色が padding の部分。親の幅が倍になると余白も倍になり、高さを倍にしても変わらない

MDN の padding のページでは、パーセントの値は「包含ブロックのインラインサイズ」に対する割合で、横書きのときは幅のことだと説明されています。CSS 2 の仕様の padding の項にも、padding-top と padding-bottom でも幅が基準だと、わざわざ書き添えられています。

margin-top も同じく幅が基準になる

margin も同じです。幅 400px・高さ 100px の親(display: flow-root を書いたもの)の中の .box に margin-top: 10% を書くと、計算後の値は 40px で、.box の上端は親の上端から 40px 下がりました。MDN の margin のページでも、パーセントの基準は包含ブロックのインラインサイズ(横書きでは幅)とされています。CSS 2 の仕様の margin の項には、margin-top と margin-bottom も同じだと書き添えられています。

margin-top: 50% では縦の真ん中に置けない

縦の中央寄せのつもりで margin-top: 50% を書くと、基準が親の幅なので、横長の親ほど大きくずれます。幅 800px・高さ 300px の親の中で、高さ 50px の .box に margin-top: 50% を書くと、margin-top は 400px になりました。.box の上端は親の上端から 400px の位置で、高さ 300px の親の外に出ています。

同じ親で、.box に position: relative; top: 50% を書くと、.box は親の上端から 150px 下がりました。こちらは親の高さ 300px の半分です。MDN の包含ブロックの説明にあるとおり、top・bottom・height のパーセントは包含ブロックの高さを、left・right・width・padding・margin のパーセントは幅を基準にします。margin と padding は、上下の値でも幅のほうに入っています。

css
.parent { width: 800px; height: 300px; } /* margin-top の検証では display: flow-root も書いた */

/* 親の幅 800px の 50% で、margin-top は 400px */
.box-margin { height: 50px; margin-top: 50%; }

/* 親の高さ 300px の 50% で、150px 下がる */
.box-top { height: 50px; position: relative; top: 50%; }
2つの場合を縦に並べた画像。上の「margin-top: 50%」では、幅600px・高さ200pxの点線の親の下に、ピンクの箱が親から離れて表示されている。下の「position: relative; top: 50%」では、水色の箱の上端が、親の上下のまん中にそろっている
幅600px・高さ200pxの親の中に、高さ40pxの箱を置いた場合。margin-top: 50% は親の幅の半分の300px、top: 50% は親の高さの半分の100px になる

top: 50% で動くのは箱の上端なので、高さ 50px の箱の中心は親の上端から 175px の位置になり、親のまん中(150px)より 25px 下に寄ります。

幅に比例した高さの箱を作れる

上下の padding が幅で決まる性質を使うと、幅に合わせて高さが変わる箱を作れます。height: 0 にして padding-top: 100% を書くと、箱の高さは幅と同じになります。親の幅が 200px のときは 200px×200px、400px のときは 400px×400px の正方形になりました。

css
.square {
  height: 0;
  padding-top: 100%;
}

縦横比は、aspect-ratio プロパティでも指定できます。padding を書かずに aspect-ratio: 1 / 1 だけを書いた箱も、親の幅が 200px のときは 200px×200px、400px のときは 400px×400px になりました。

基準になる「親の幅」は、どの箱の幅か

パーセントの基準は、正確には包含ブロックの幅です。多くの場合はすぐ外側の要素の中身の幅ですが、置き方によって変わります。それぞれの置き方で padding-top: 10% を書いて測りました。

置き方 基準になった幅 padding-top
幅 600px のフレックスコンテナの中の、幅 200px のアイテム コンテナの幅 600px 60px
幅 600px のグリッド(列は 200px と 400px)の1列目のアイテム 列の幅 200px 20px
同じグリッドの2列目のアイテム 列の幅 400px 40px
position: relative で幅 400px・padding: 50px の親の中の、通常の要素 親の中身の幅 400px 40px
同じ親の中の、position: absolute の要素 親の padding を含めた幅 500px 50px

フレックスボックスのアイテムでは、アイテム自身の幅(200px)ではなく、コンテナの幅が基準でした。Flexbox の仕様には、フレックスアイテムのパーセントの margin と padding も、ブロックの箱と同じく包含ブロックのインラインサイズを基準にすると書かれています。

グリッドのアイテムでは、グリッド全体の幅(600px)ではなく、アイテムが入っている列の幅が基準になりました。Grid の仕様では、グリッドアイテムの包含ブロックは、そのアイテムが置かれたグリッドエリアです。同じ padding-top: 10% でも、列の幅が違えば余白の大きさも変わります。

position: absolute の要素では、親の padding まで含めた幅(400px+左右 50px ずつ)が基準になりました。MDN の包含ブロックの説明に、absolute の要素の包含ブロックは、position が static 以外の直近の祖先のパディングボックスの辺で作られる、と書かれているとおりです。

縦書きでは、高さが基準になる

基準は「幅」と覚えがちですが、仕様の言い方は「包含ブロックのインラインサイズ」です。インラインサイズは、文字が並ぶ向きの長さです。親に writing-mode: vertical-rl を書いて縦書きにすると、文字は上から下へ並ぶので、インラインサイズは高さになります。

幅 400px・高さ 200px の縦書きの親の中で、.box に padding: 10% を書くと、上下左右の padding はすべて 20px でした。親の高さ 200px の10%で、横書きのときとは逆に、幅 400px は基準になっていません。CSS Writing Modes の仕様には、CSS 2.1 では幅を基準にしていた margin と padding のパーセントが、CSS3 ではインラインサイズを基準にする、と書かれています。

​Posted by 86