CSSのpaddingとmarginを%で書くと上下も親の幅で決まる
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)にはなりません。
.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 のままでした。

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 は、上下の値でも幅のほうに入っています。
.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%; }

top: 50% で動くのは箱の上端なので、高さ 50px の箱の中心は親の上端から 175px の位置になり、親のまん中(150px)より 25px 下に寄ります。
幅に比例した高さの箱を作れる
上下の padding が幅で決まる性質を使うと、幅に合わせて高さが変わる箱を作れます。height: 0 にして padding-top: 100% を書くと、箱の高さは幅と同じになります。親の幅が 200px のときは 200px×200px、400px のときは 400px×400px の正方形になりました。
.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 ではインラインサイズを基準にする、と書かれています。





