はじめに
この記事では、HTML / CSSを使ったWeb開発において、「画像の大きさ」を制御するには意外とたくさんのことを考えないといけないことについてまとめます。
なおこの記事ではsrcset属性やsizes属性、picture要素などを使ったAdaptive imagesについては触れません。Adaptive imagesとは、閲覧環境によって適切な画像を選択的に読み込めるようにするものです。この記事では、これから読み込む/もう読み込んだ画像の大きさを制御することについて考えていきます。
過去には、「Webアプリのダークモード対応って地味に難しいよ」という話という記事も書きました。適切な実装のためには、単純そうに見えることにも意外と考えなければならないことがたくさんあるため大変なものです。
5段階の「画像の大きさ」
まずは「画像の大きさ」という概念について整理します。ひとくちに大きさと言っても実際はいくつかの段階がある、と捉えると便利です。
- 画像の縦横のピクセル数
- HTMLでの
width属性とheight属性によって指定された大きさ - CSSで指定された画像を表示するコンテナーの大きさ
- CSSで
object-fitによって指定された、コンテナーへの画像のはめ込み方 - 最終的に描画される大きさ
「画像の大きさ」という概念は複数の技術領域にまたがって存在し、段階を経るごとに意味合いが微妙に変わってきます。一つずつ見ていきましょう。
画像の縦横のピクセル数
画像の縦横のピクセル数は、「画像サイズ」と言った際に一般的に指し示されているもののことでしょう。「横1920ピクセル・縦1080ピクセル」のような解像度を持った画像が一般的ですが、これ以外のピクセル数・縦横比ももちろん存在します。
これは画像ごとに決まっていて、不変です。
HTMLのwidth・height
HTMLで画像を表示する際には、img要素を用います。img要素などにはwidth属性とheight属性というものを持たせることができますが、これはその要素が画面上で表示される際の幅と高さを指定するためのものです。
単位はCSSピクセルで、指定できる値は非負整数に限られます。後述するCSSのように単位を使っては指定できず、相対的な長さを使うこともできません。例えば、width="300"と書いた場合、「300CSSピクセルの幅で表示したい」という意味になります。
The width and height attributes may be specified to give the dimensions of the visual content of the element (the width and height respectively, relative to the nominal direction of the output medium), in CSS pixels. The attributes, if specified, must have values that are valid non-negative integers.
[日本語訳]
widthとheight属性は、要素の視覚的コンテンツの大きさ(それぞれ出力媒体の公称方向に対する幅と高さ)をCSSピクセル単位で指定するために指定できます。これらの属性を指定する場合、有効な非負整数値でなければなりません。
これら属性によって指定するサイズと、前述の画像ファイルそのものが持っている本来のサイズを一致させる必要はありません。前述の通り、画像自体も「横1920ピクセル・縦1080ピクセル」のような解像度を持ってはいます。しかしこれらの属性で指定するのはCSSピクセルです。そもそも、CSSピクセルは物理的なピクセルと必ずしも同じではなく、閲覧環境や出力媒体によって変わり得るものです。
ただし、アスペクト比は一致させる必要があります。HTML Standardでは、「画像の元の縦横比と属性で指定された幅と高さの比率が(ほぼ)一致していなければならない」という制約があります。一致していない場合、画像が不自然に引き伸ばされたり、潰れたりする指定になってしまうため、HTML Standardでは認められていないのです。(実際には、これらの属性を使って画像を潰して表示することは可能ですが、少なくとも「これらの属性はそのように設計された機能ではない」というところには注意を払う必要があります。)
これらの属性を使っても表示する画像の大きさを制御可能ではあり、縦横比を維持していれば好きなようにして構わないとなっていますが、実際にこれを使ったスタイリングは一般的ではありません。CSSから、便利な単位を使ったり、他の様々なスタイルやメディアクエリと連携したりする形で指定するほうが圧倒的に柔軟で便利です。例えばレスポンシブ対応は、属性だけでは現実的に不可能です。
実務上は、
width属性とheight属性はレイアウトシフトを防ぐためのものである。- 実際の表示サイズはCSSで制御する。
という解釈をするとよいでしょう。
レイアウトシフトとは、画像の大きさが事前に明らかにされていない状況で、画像の読み込み前後でレイアウトがズレてしまう現象のことです。
heightとwidthを記載することで、画像を読み込む前にブラウザーが画像のアスペクト比を計算することができるようになります。このアスペクト比は、画像を表示するために必要な空間を確保するために使用され、画像をダウンロードして画面に表示したときのレイアウトのずれを縮小したり、防止したりすることができます。レイアウトのずれを縮小することは、良い使い勝手とウェブパフォーマンスの主要な構成要素です。
MDNの説明
UXを確保する上では、width属性とheight属性の両方を指定することはほぼ必須です。Webアプリで画像についてのメタデータを渡すようなAPIを開発している場合は、その画像のピクセル数を数えて教えてあげる処理を実装しておくことが好ましいでしょう。しかし実際の表示サイズはCSSで制御します。
CSSによるコンテナーサイズの指定
さて、その実際の表示サイズを決定するのが、CSSによるwidthやheightの指定です。pxや%などの長さの単位を使って指定します。ただしここで指定されるのは画像のコンテナーサイズであり、そのコンテナー内にどう画像がはめ込まれて表示されるかは、後述のobject-fitプロパティによって指定されます。
現代のCSSでは、書字方向(writing-mode)を考慮して幅や高さを指定するプロパティとして、inline-sizeやblock-sizeがあります。現代のWebでの日本語表示では、一般的に「左から右への横書き(horizontal-lr)」が使われていますが、この場合、inline-sizeは従来のwidthに、block-sizeは従来のheightに対応しています。
しかし、こと画像においては、こういった書字方向ベースの長さ指定について考えなければならない場面はあまり多くはないでしょう。コンポーネント全体を論理プロパティで統一して設計している場合には、画像もその流れに乗せる、という判断はあり得ますが。
置換要素という概念とobject-fit
CSSのobject-fitは、コンテナー内への置換要素(今回の場合は画像)のはめ込みの仕方を指定する際に使われるプロパティです。CSSによってコンテナーサイズは指定したわけですが、ここに画像をどうはめ込むかはまた別の問題となっています。それを制御するのがこのプロパティです。
ウェブ開発において置換要素とは、そのコンテンツが外部リソースまたは文書構造外で定義されたコンテンツによって置き換えられ、CSSレンダリングモデルの対象外となるHTML要素を指します。これらは、その表現がCSSの整形モデルから独立した外部オブジェクトです。
Replaced elements (置換要素) - MDN Web Docs 用語集 | MDN
主な使い方は次の3つになるでしょうか。(これ以外にもありますが、ここでは割愛します。)
object-fit: fill;- アスペクト比は維持されなくていいから、画像全体でコンテナーを埋めたい。
- アスペクト比を無視して画像を変形させるという性質は、一般的に期待される写真やイラストの見え方とは相容れないため普通は使われない。
- 正方形などの決め打ちな形に機械的に生成されたプレースホルダー画像を、コンテナーいっぱいに広げて表示するときに使う?
object-fit: cover;- 画像は見切れてもいいので、アスペクト比を維持しつつコンテナーを埋めたい。
object-fit: contain;- コンテナーは埋まらなくていいので、アスペクト比を維持しつつ画像を見切れさせることなくコンテナー内に配置したい。

最終的に描画される大きさ
JavaScriptからgetBoundingClientRectなどを使うことで、コンテナーサイズをピクセル数として確認可能です。(が、これをもとにスタイルをJavaScriptから動的に変更するということはまずないでしょう。パフォーマンスも悪いですし、下手な実装では無限ループに陥ります。)
実務上の設計方針とリンクカードの例
- 画像の縦横のピクセル数は、画像の特性によって変えることになります。データ量を抑えるために小さめにリサイズするなどすれば当然変わります。
- HTMLでの
width属性とheight属性による指定は、レイアウトシフトを防ぐためのものです。アスペクト比さえ合っていれば、画像の実際のピクセル数を気にしすぎる必要はありませんが、現実的な値を設定しておくのが無難でしょう。 - CSSでのコンテナーサイズの指定は、レイアウト上の要件に合わせて考えていくことになります。このサイズを超えて画像が表示されることはありません。
- CSSでの
object-fitによる指定は、コンテナー内にどう画像をはめ込むかを指定するものです。アスペクト比を維持するかどうか、見切れることを許容するかどうか、コンテナー内部を埋めきるかの3つの尺度で考えることになります。 - これらによって、最終的に描画される大きさが決まります。この大きさはJavaScriptから取得可能です。これはHTML属性やCSS指定とは別の「結果」として扱うべきものです。
ではコンテナーサイズを画像のサイズで可変にしたいという場合はどう考えるべきでしょうか。例として、リンクカードのコンポーネントを考えてみましょう。これは、URLからタイトルやOGP画像を取得し、カード型にまとめたコンポーネントです。この際の画像は任意のピクセル数・任意の縦横比を持ち得ます。

この場合の考え方としては、「コンテナーを完全に固定する」のでも「画像にすべてを委ねる」のでもなく、レイアウト側が一つの軸だけを制御し、もう一方は画像のアスペクト比に委ねる、という捉え方です。
コンテナーサイズを画像に追従させたい、という要件は、実際には「二次元の自由度を一つに制限したい」という意味になります。横と縦の両方をレイアウトで決めてしまうと、必ずobject-fitによるトリミングや余白処理が必要になりますが、どちらか一方だけを決めれば、残りは自然とアスペクト比で決まります。これが、画像サイズを起点にしたレイアウトにおける基本的な考え方です。
リンクカードの例では、カード全体の高さをレイアウト都合で固定し、その中で画像が占める高さを決め(画像要素にCSSで高さだけを指定し)、幅はautoにしておくことで、画像の自然なアスペクト比に従って横幅が決まります。ただし、横に無制限に広がってしまうとカードレイアウトを壊す可能性があるため、max-widthを設ける必要があるでしょう。また、やけに縦に長い画像の場合の見栄えも考えると、min-widthも欲しいところです。(そしてそれらが使われた際の見栄えのためにはobject-fit: cover;が必要です。)
<!-- 非常に簡略化したHTMLの例 -->
<a class="link-card" href="https://www.example.com/" target="_blank" rel="noopener noreferrer">
<div>
<h1>Lorem Ipsum</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<p>www.example.com</p>
</div>
<img class="thumbnail" src="./thumbnail.webp">
</a>
/* 非常に簡略化したCSSの例 */
.link-card {
container-type: size;
height: 100px; /* 今回は決め打ち */
display: flex;
}
.thumbnail {
height: 100cqh;
max-width: 191cqh; /* 1.91:1のよくあるアスペクト比を上限とする */
min-width: 100cqh; /* 1:1のアスペクト比を下限とする */
object-fit: cover; /* `max-width`や`min-width`が使われた際の見栄えのために */
}
この設計では、画像は「高さに合わせて伸縮する要素」となり、横方向は画像ごとの差異をそのまま受け入れる形になります。この結果、縦横比の極端な画像では、上限いっぱいまで広がったり、逆に1:1になるよう切り抜かれたりしますが、それは仕様として許容することになるでしょう。
おわりに
「画像の大きさ」という概念は、ピクセル数から最終的に描画される大きさまでの間にもいくつかの段階があると捉え、それぞれの段階の設計方針について整理することで、Web開発において「画像の大きさ」を制御するには意外とたくさんのことを考えないといけないことについてまとめました。適切な実装のためには意外と考えさせられて、なかなか大変ですね。