top,left,translate,padding-topなどに指定するパーセント値が何に対する割合なのかを理解する

topやleftなど「距離や長さ」に関するCSSプロパティで値にパーセント値を指定したとき、それが「何に対する割合」なのか私は理解があやふやだったので、この記事では理解するためのポイントを整理したいと思います。例として次の４つのケースを取り上げ、プロパティ値に100%を指定したときに要素の配置や寸法が何を基準にどのように変化するかを図で確認してみましょう。

* position: absoluteのときのtop, left
* position: relativeのときのtop, left
* transform: translate関数の引数X | Y
* padding-top (ブロック要素の高さを確保するのに用いる）

記事中のサンプルのソースコードはこちら。
DEMO: [https://codepen.io/kaz_hashimoto/pen/LYPLdPy](https://codepen.io/kaz_hashimoto/pen/LYPLdPy)

## 例1. position: absoluteのときのtop, left: 100%
最初の例は、`position: absolute`を設定した子要素を`top: 100%; left: 100%`で配置した場合です（下図）。親要素div.box-1の中に２つの子要素div.child-1, div.child-2を入れ、どちらの子要素もwidth, heightを50%に設定しました。そして、.child-1のみ`position: absolute`を設定し、.child-2は通常のフローで配置し`translate`を使って再配置しています。再配置によって子要素がどこからどこまで移動したのかわかるようにアニメーションにしてあります。.box-1の左上角あたりが元の位置（top:0, left:0, translate(0,0)）ということになります。
図で.box-1のコンテンツ領域を破線で示してあります(.content-box)。
![01_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/0e5450a3-ba14-8e88-e59f-d8338c2f8669.gif)

```html:html
<div class="box-1">
  <div class="child-1 child-box"><p>width, height 50%<br>top, left 100%</p></div>
  <div class="child-2 child-box"><p>width, height 50%<br>translate 100%</p></div>
</div>
```

<details><summary>CSS (要点のみ）</summary><div>

```css:css
.box-1 {
  width: 300px;
  height: 100px;
  padding: 20px;
  border: 16px solid rgba(0,0,0,0.3);
  background-color: #B3E5FC;
  position: relative;
}

.child-box {
  box-sizing: border-box;
  width: 50%;
  height: 50%;
  border: 1px solid #000;
}

.child-1 {
  background-color: #E91E63;
  color: #fff;
  position: absolute;
  top: 100%;
  left: 100%;
}

.child-2 {
  background-color: #3F51B5;
  color: #fff;
  transform: translate(100%, 100%);
}
```
</div></details>
ここで注目すべきは、.child-1のサイズと元の位置です。２つの子要素はどちらもwidth, heightを50%に設定しましたが.child-1の方が.child-2より大きいですね。つまり、`position: absolute`を設定した.child-1の方は、

* width, heightがそれぞれ.box-1の**パディング領域**のwidth, heightの50%になっている。
* 元々の位置(top: 0, left: 0)は.box-1の**パディング領域**の左上角になっている。
* top,leftを100%にした結果、元の位置から.box-1の**パディング領域**の幅と高さの分だけ離れた位置に配置されている。

一方、通常フローで配置した.child-2の方は、

* width, heightがそれぞれ.box-1の**コンテンツ領域**のwidth, heightの50%になっている。
* 元々の位置(translate(0,0))は.box-1の**コンテンツ領域**の左上角になっている。

ことがわかります。(※translateについては例3に書きます）

`position: absolute`の場合の計算根拠をCSSの仕様書で確認してみましょう。たとえばwidthに関して、[10.2 Content width: the 'width' property](https://www.w3.org/TR/CSS22/visudet.html#the-width-property)の注記によると確かに、
> Note: For absolutely positioned elements whose containing block is based on a block container element, the percentage is calculated with respect to the width of the **padding box** of that element.

ここで包含ブロック(containing block)の定義を確認しておくと、[10.1 Definition of "containing block"](https://www.w3.org/TR/CSS22/visudet.html)の説明では
>If the element has 'position: absolute', the containing block is established by the nearest ancestor with a 'position' of 'absolute', 'relative' or 'fixed', in the following way:
>（中略）
> 2. Otherwise, the containing block is formed by the **padding edge** of the ancestor. 


### 例1a. 親と子要素の間にinner divがある場合
例1で、.box-1の`position: relative`はそのままに、.box-1の内側にラッパーとなるdiv.innerを追加して、.child-1と.child-2を.innerの子要素(.box-1の孫）にした場合はどうなるでしょうか？（下図）
![01a_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/c805577b-de1d-4ba5-f92f-f6d66b90126a.gif)

```html:html
<div class="box-1">
  <div class="inner">
    <div class="child-1 child-box"><p>width, height 50%<br>top, left 100%</p></div>
    <div class="child-2 child-box"><p>width, height 50%<br>translate 100%</p></div>
  </div>
</div>
```
<details><summary>CSS (要点のみ）</summary><div>

```css:css
.box-1 { /* 例1と同じ */ }
.child-box { /* 例1と同じ */ }
.child-1 { /* 例1と同じ */ }
.child-2 { /* 例1と同じ */ }

.box-1 .inner {
	width: 260px;
	height: 80px;
	outline: 3px solid red;
	outline-offset: -3px;
	background-color: transparent;
}
```
</div></details>

結果は、.child-2はサイズが.innerに合わせて小さくなりましたが、.child-1の方はサイズ・位置ともに例１から変化しないことがわかります。理由は、上述の包含ブロックの定義によると、.child-1から見て「the nearest ancestor with a 'position' of 'absolute', 'relative' or 'fixed'」（position の値が static 以外の直近の祖先要素）は.box-1のままなので、.child-1は.innerのサイズに影響を受けないためです。

## 例2. position: relativeのときのtop, left: 100%
次に、例1で.child-1の`position`の値を`absolute`から`relative`に変えてみます（下図の.child-3）。
![02_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/566ee06f-f785-75c9-00cd-416b9a330b3b.gif)

```html:html
<div class="box-1">
  <div class="child-3 child-box"><p>width, height 50%<br>top, left 100%</p></div>
  <div class="child-4 child-box"><p>width, height 50%<br>top, left 100%</p></div>
</div>
```

<details><summary>CSS (要点のみ）</summary><div>

```css:css
.box-1 { /* 例1と同じ */}
.child-box { /* 例1と同じ */}

.child-3,
.child-4 {
  color: #fff;
  position: relative;
  top: 100%;
  left: 100%;
}

.child-3 { background-color: #004d40; }
.child-4 { background-color: #009688; }
```
</div></details>
要素のサイズと位置に関して、.child-3と例1の.child-1との違いに着目してください。width, heightは50%でどちらの子要素も同じですが、.child-3の方が.child-1より小さくなりましたね。つまり、`position: relative`を設定した.child-3の方は、

* width, heightがそれぞれ.box-1の**コンテンツ領域**のwidth, heightの50%になっている。
* 元々の位置(top: 0, left: 0)は.box-1の**コンテンツ領域**の左上角になっている。
* top,leftを100%にした結果、元の位置から.box-1の**コンテンツ領域**の幅と高さの分だけ離れた位置に配置されている。

図で、.child-4は.child-3の後続要素であり、同じく`position: relative`を設定しています。.child-3と相対的な位置関係を維持したまま再配置されています。

上記の計算根拠をCSSの仕様書で確認してみましょう。widthに関して、[10.2 Content width: the 'width' property](https://www.w3.org/TR/CSS22/visudet.html#the-width-property)によると、
> The percentage is calculated with respect to the width of the generated box's containing block. 

topに関して、[9.3.2 Box offsets: 'top', 'right', 'bottom', 'left'](https://www.w3.org/TR/CSS22/visuren.html#position-props)によると、
> The offset is a percentage of the containing block's width (for 'left' or 'right') or height (for 'top' and 'bottom'). 

で、`position: relative`の要素に対する包含ブロックの定義を確認すると、[10.1 Definition of "containing block"](https://www.w3.org/TR/CSS22/visudet.html)にあるとおり、

> 2.For other elements, if the element's position is 'relative' or 'static', the containing block is formed by the **content edge** of the nearest ancestor box that is a block container or which establishes a formatting context. 

## 例3. translate(X%, Y%)
例1で示したように、translate(X%, Y%)のX, Yは、要素自身のボーダー含む幅・高さに対する割合です。ちなみに、親要素のwidth, heightがautoでないとき、子要素のpositionとtranslateのパーセントに50%を指定することにより、親要素の縦横中央にブロック要素を配置することができます（下図）。
DEMO: [https://codepen.io/kaz_hashimoto/pen/vYBWLZz](https://codepen.io/kaz_hashimoto/pen/vYBWLZz)

```css:css
.center {
  width: 200px;
  height: 80px;
  position: absolute;
  top: 50%;  /* 包含ブロックのパディング領域の縦中央*/
  left: 50%; /* （同）横中央*/
  transform: translate(-50%, -50%);  /* 要素自身の幅・高さの半分だけ位置をずらす */
}
```

![div-center_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/74801b58-ae0b-659b-6ac2-866c97cecb82.gif)

transformプロパティのパーセント値についてCSSの仕様書で確認してみましょう。[4. The transform Property](https://www.w3.org/TR/css-transforms-1/#transform-property)によると、Percentagesは
> refer to the size of reference box

reference boxとは[6. Transform reference box: the transform-box property](https://www.w3.org/TR/css-transforms-1/#transform-box)によると、transform-boxプロパティの値がたとえば`border-box`の場合、
> Uses the border box as reference box.

ブラウザの開発ツールからtransform-boxの初期値を調べると、border-box (Firefox, Safari), view-box (Chrome, Opera)になっていました。仕様書の上記説明の補足に以下の記載があるので、Chrome/Operaの動作もborder-boxとみなしてよいのかも？

> For elements with associated CSS layout box, the used value for fill-box is content-box and for stroke-box and view-box is border-box.

## 例4. padding-top: 100%
padding-topにパーセント値を指定した場合は、包含ブロックのコンテンツ領域の**幅**に対する割合になります（下図）。幅が可変の親要素の中に、縦横比が一定の領域を確保してブロック要素を配置したいときなどにこのテクニックが役立ちます。（例：`width: 100%; padding-top: 75%;`）

![03_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/469745/224aafc5-00e9-1a23-223b-177d83a146c6.gif)

```html:html
<div class="box-2">
  <div class="child-5"><p>padding-top<br>100%</p></div>
</div>
```

<details><summary>CSS (要点のみ）</summary><div>

```css:css
.box-2 {
  width: 200px;
  height: 100px;
  padding: 10px 70px;
  border: 16px solid rgba(0,0,0,0.3);
  background-color: #B3E5FC;
}

.child-5 {
  box-sizing: border-box;
  padding-top: 100%;
  width: 50%;
  line-height: 1.4;
  background-color: #FF5722;
  border: 1px solid #000;
}
```
</div></details>
padding-topプロパティのパーセント値についてCSSの仕様書で確認してみましょう。[8.4 Padding properties: 'padding-top', 'padding-right', 'padding-bottom', 'padding-left', and 'padding'](https://drafts.csswg.org/css2/box.html#propdef-padding-top)によると、確かにこう書かれてます。
> The percentage is calculated with respect to the **width** of the generated box's containing block, even for 'padding-top' and 'padding-bottom'.