なぜ<img>はインライン要素なのに上下の margin が適用されるのか?
ある日の出来事
このようなコードをレビューに提出しました。
(説明に必要ないところは抽象化しています。)
<section class="space-y-4">
<div>
<img class="hidden lg:block" alt="デスクトップ用画像" />
<img class="lg:hidden" alt="モバイル用画像" />
</div>
<div><!-- 何かのコンテンツ --></div>
<div><!-- 何かのコンテンツ --></div>
<div><!-- 何かのコンテンツ --></div>
</section>
画像をモバイルとデスクトップで分ける必要があったので 2 つの<img>
が必要でした。
また、親要素にspace-y-4
が指定されているため不要な余白が入らないように<img>
を<div>
で囲いました。
レビューに提出すると、この箇所で以下コメントをいただきました。
img はデフォルトで
inline
なので、lg:block
とデフォルトので差がありそう。それでdiv
で囲ったのかもしれないけど
このコメントを読み「確かに!」と思い、修正してみましたがうまくいきませんでした。
なぜか。上下の margin
が適用されています。
右下の Devtools の Computed の display
は inline
なのに、
Code Pen でも再現しました。
margin-top
が適用されています。
原因
最初はフクロウセレクタの結合子とかの影響かなと考えていましたが、
気になり調べた結果 stack overflow にこんな質問がありました。
簡単に内容を翻訳すると
質問
I am wondering why img tag accept margin top property? isn't this an inline tag? and inline tags does't accept top and bottom margins?
なぜimg
タグはmargin top
プロパティを受け入れるのでしょうか?これはインラインタグではないのですか?
回答
It's because img is an inline replaced element and it does accept margin-top. It behaves differently than inline non-replaced element (like span for example).
img
はインライン置換要素であり、margin-top
を受け入れるからです。これは、インラインの非置換要素(たとえばspan
など)とは異なる動作をします。
どうやら、置換要素が原因ということはわかりました。
ただ、なぜ置換要素だと上下の margin
が適用されるかまではわからなかったのでさらに調べてみました。
ブロック要素とインライン要素の違い
まずは、ブロック要素とインライン要素の違いについてです。
ブロック要素
デフォルトで幅が親要素を満たし(width: 100%
)、新しい行から始まり、幅と高さの指定が可能です。
さらに上下の margin を適用できます。
インライン要素
コンテンツの流れに沿って配置され、幅と高さは内容によって自動的に決定されます。(width: auto
)
インライン要素では上下の margin
は適用されません。
置換要素とは
置換要素とは、その内容が外部によって提供される要素です。
HTML タグで言うと<img>
,<iframe>
,<video>
など外部のリソースを表示する際に用いられるものです。
これらの要素は、内部コンテンツに基づいて固有の幅と高さを持つことができます。
また、置換要素は、ブロックレベル要素とインライン要素の間にある特別なカテゴリと見なすことができ、特定の条件下で上下の margin を適用できます。
イメージで言うとブロック要素とインライン要素の中間のような要素。
ただ、display
は、あくまで inline
です。
inline-block
やないかい」
🥛👶「その特徴はそうです、置換要素は限りなく inline-block
のような振る舞いをします。
でも、置換要素は inline
なのです。
なぜこんなややこしいことになってしまったのか。
CSS の歴史を振り返れば理解できます。
CSS の歴史
第一章 生命の誕生
生命の始まりは、まだ太陽の光が地上に届かない頃、間欠泉の地下で始まりました。
間欠泉の地下では、生命の材料となる様々な分子「生命構成単位」が出来上がっていきました。
間欠泉内部では、100°C になると地上への噴出により水が入れ替わります。
これにより水は 100°C を超えることはなく、作られた分子は守られました。
また、地下で還元、地上で酸化の場を提供したことも分子の合成には不可欠でした。
このような乾湿サイクルの場は、生命構成単位を合成する大切な場でした。
脂肪酸が集まり生命を包む膜となり、ウェットとドライな状態が繰り返されることで重合反応が進み触媒活性を持つタンパク質原始物質が作られました。
そして、これらの分子が循環し、混じり合うことによって、さらに複雑な分子へと発展しました。
生命を包む分子「原始 RNA」がさらに「酵素様原始物質」と混じり合い、自己複製機能を持つ「リボザイム」に進化します。
リボザイム
こうして分子は、生命の「配列」を複製する力を身につけました。
そして、これらが脂質の膜に取り込まれ「原始生命体」が生まれました。
第二章 初期の CSS
初期の CSS では、display
プロパティの値は block | inline | list-item | none
しかありませんでした。
現代では考えられませんが、これらの値だけで画面ができていたのです。
この時から<img>
も置換要素として存在しました。
置換要素とは、先述した通りに
イメージで言うとブロック要素とインライン要素の中間のような要素。
です。
固有の幅と高さを持つことができ、上下の margin も適用できます。
しかし、この時代にはinline-block
は存在しないので
🥛👶「その特徴は
inline-block
やないかい」
と言うツッコミは、的を得ていません。
第三章 CSS2 の登場
CSS2 でやっと inline-block
が登場しました。
これは筆者の想像ですが、置換要素は inline
なのに block
のような振る舞いをしてわかりづらいから、ブロック要素とインライン要素の中間となる inline-block
作ったら便利じゃね?となったのではないかと考察しています。
しかし、inline-block
が登場したものの<img>
などの置換要素は相変わらずdisplya: inline
のままでした。
この状況は現代でも続いています。
これは、後方互換性を担保するためです。
現にドキュメント内で
'インラインブロック'、通常のフローにおける置換要素
インライン置換要素と全く同じ。
と記載されています。
結論(まとめ)
-
<img>
に上下の margin が適用されるのはインライン置換要素だから。 -
inline-block
は初期の CSS では存在せずinline
なのにblock
のような振る舞いをしていた。 - CSS2 で
inline-block
が登場して、インライン置換要素の立場がややこしくなった。
余談
ちなみに Tailwind CSS ではインライン置換要素はデフォルトで display: block
になっているようです。
おわりに
今回は、なぜ<img>
に上下の margin が適用されるのか調べてまとめてみました。
過去の CSS のドキュメントを読むなんてことはなかったので勉強になりました。
可能な限り裏どりはしましたが、何か間違っている箇所があれば指摘していただけるとありがたいです。
参考文献
Discussion