まえがき
こちらの記事 でわかりやすくまとめられていますが、CSSで縦横比(アスペクト比)を維持するテクニックで「padding-top:50%」なるものを親要素の:beforeに指定することがありますよね。
この「padding-top: 50%」やらその周辺がよくわからなかったので、調べてみました。
See the Pen Keep Aspect Ratio by 熊瀬川直也 (@momonoki1990) on CodePen.
1. position:abosoluteは「浮く」
順番に紐解いていきます。
position: ablosluteは絶対位置を指定するんですね。先祖要素にposition: static以外の値が指定されている(大抵relative)時はその先祖要素が基準に、そうでない時はウィンドウ全体の左上が基準位置となります。
ここでポイントなのが、position: absoluteを指定した要素が「浮く」ということです。
どういうことかというと、
position: absolute や position: fixed が指定された要素は、通常のレイアウト処理から除外されるため、あたかも要素が存在していないかのように後続要素や親要素がレイアウトされます。
position: absolute;した親要素の高さがなくなるのを解消する一般的な(clearfixのような)方法はありませんか?
それで、absoluteを指定された要素、つまり上の例で言えば、<div id="content">が浮くと、何が起こるかというと、<div id="wrapper">が高さを持たなくなる(つぶれる)んです。
(試しに、上のコードの#wrapper:beforeで指定しているCSSと#contentのposition: absoluteを消してみてください)
ではなぜ<div id="content">が浮くと親要素の<div id="wrapper">が高さを持たなくなってしまうかというと、<div id="content">が浮く = <div id="wrapper">が子要素を持たなくなる、つまり、<div id="wrapper"></div>と書いているのと一緒になるからです。
親要素の高さは子要素の高さ+上下paddingの値です。
つまり、<div id="wrapper">は子要素を持たず、paddingも指定されていないと、高さが0となり、潰れてしまうんですね。
2. :beforeの挿入位置とdisplay
#wrapper:before で指定しているCSSは何をしているのか。
そもそも:beforeですが、
CSSの::afterと::beforeは、疑似要素と呼ばれるものの1つです。これを使うと「HTMLには書かれていない要素もどきをCSSで作ることができる」のです。
content:''の中には挿入したい文字などを入れます。
CSSの疑似要素とは?beforeとafterの使い方まとめ
※saruwakaさんのページ、擬似要素の使用事例などめちゃくちゃわかりやすいです。
そして、私もよくわからず、なんとなく:beforeは「指定したタグの前になんか入れるんでしょ」と思っていたんですが、厳密には、
E:before … 要素Eの最初に架空のインライン要素として、contentで指定した内容を挿入する
E:after … 要素Eの最後に架空のインライン要素として、contentで指定した内容を挿入する
コンテンツ更新に強い!CSS擬似要素:beforeと:afterの使い方
とのことで、contentで指定した内容を、:beforeを指定した要素(<div id="wrapper">)の"中身の最初"に挿入するんですね
(つまり、<div id="wrapper">の子要素として:beforeで作られた擬似要素content: ''が挿入される)
使ってれば確かに当たり前のことですが、なんとなく#wrapper:beforeなんで、<div id="wrapper">タグの前に挿入される、と思っていました。
実際、検証ツールを見ると、:beforeという表示は、:beforeを指定したタグの開始タグの直後に入っているのがわかります。
そして、:beforeで作られた擬似要素は、先ほどの引用にもありましたが、インライン要素となります。
インライン要素も、paddingはとります。
(ただし、上下paddingは他の要素との重なりを考慮しないので、実際はあまり使えない 参考 )
しかし、中身のない(content: '')インライン要素は、paddingをとりません。
そして、ブロック要素は、例え中身がなくても、paddingをとることができるのです。
(ブロック要素はデフォルトで、それ自体が幅を持つ(親要素の幅いっぱいに広がる)からですかね)
試しに、上のコードの/*display: inline;*/のコメントアウトを外してみてください。
padding: 20pxで押し広げた領域がなくなります。
なので、:beforeで空要素を設けて、その空要素をpadding-topで押し広げ、結果的に親要素の<div id="wrapper">の高さを出すために、#content:beforeにdisplay: blockを指定してるんですね。
3. 上下paddingは親要素の横幅が基準
すでに書いてしまいましたが、padding-top: 10%は、結果的に、親要素の<div id="wrapper">の高さを出すために指定されています。
では、10%とは一体なんなのか。
W3C勧告によると、margin/paddingのパーセント指定は上下方向についても要素の横幅が基準になってるんですね。
CSSの上下パーセント指定が難しいのは親要素の幅で決まるから
ということで、padding-top: 10%の基準になっているのは、親要素、つまり、<div id="wrapper">の横幅ということになります。
試しに、#wrapperセレクタのCSSに、width: 50%を足すと、<div id="wrapper">の幅が半分になるとともに、高さも半分になるのがわかります。
こういった仕組みで、縦横比(アスペクト比)が維持されるようになっているんですね。
あとがき
CSSの元文献ってどこにあるんだろうか。ググって出てきたサイトの説明が微妙に間違ってたりするのでもどかしい。
参考
縦横の比率維持で迷うのはもうやめよう!
https://qiita.com/fukamiiiiinmin/items/b8c8c190f2de492064de