CSSで、縦横比をそろえて画像を並べる方法

​​CSS​HTML

縦長・横長・正方形が混ざった画像を、同じ形にそろえて並べるには、img に aspect-ratio: 4 / 3 と object-fit: cover を付けます。幅は並べる枠に合わせて決まり、高さは aspect-ratio の比から決まるので、どの画像も同じ大きさの 4:3 になります。比が合わない部分は、画像を引き伸ばさずに切り取ります。

補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。HTML と CSS だけのページを Playwright で開き、getBoundingClientRect() と getComputedStyle() の値、画面を撮った画像の色を記録しました。画像は、検証のために描いた縦横比の違う6枚の SVG です。Firefox と Safari、スマートフォンの実機は試していません。

上から3つの並べ方を比べた画面。「aspect-ratio なし」では、6枚の画像が元の形のまま並び、高さがばらばらで、短い画像の下に白いすき間がある。「aspect-ratio: 4 / 3 と cover」では、6枚とも同じ大きさの横長の四角になり、縦長の画像は上下の端、横に長い画像は左右の端が切り取られている。「aspect-ratio: 4 / 3 と contain」では、6枚とも同じ大きさの枠の中に画像全体が縮小して入り、余った部分は薄い水色になっている
縦横比の違う6枚の画像を、3つの CSS で並べた画面

img に aspect-ratio と object-fit: cover を付ける

HTML は、<ul> の中に画像を並べただけです。width と height の属性には、それぞれの画像の元の大きさを書いています。

html
<ul class="gallery">
  <li><img src="img/landscape.svg" alt="横長の画像" width="600" height="400"></li>
  <li><img src="img/portrait.svg" alt="縦長の画像" width="400" height="600"></li>
  <li><img src="img/square.svg" alt="正方形の画像" width="500" height="500"></li>
  <li><img src="img/wide.svg" alt="横に長い画像" width="800" height="300"></li>
  <li><img src="img/photo.svg" alt="4:3の画像" width="640" height="480"></li>
  <li><img src="img/tall.svg" alt="縦に長い画像" width="360" height="640"></li>
</ul>

CSS は次のとおりです。

css
.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.gallery img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.gallery はグリッドで3列に並べ、画像は width: 100% で列の幅いっぱいにしています。高さは height: auto なので、aspect-ratio に書いた 4:3 の比から決まります。

画面の幅を 400px にして測ると、6枚とも幅 122.66px、高さ約 92px でした。幅を 800px にすると、6枚とも幅 256px、高さ 192px でした。どちらも幅と高さの比は 4:3 です。

aspect-ratio と object-fit を外すと、幅は同じ 122.66px のまま、高さは横に長い画像の 45.98px から、縦長 9:16 の画像の 218.05px までばらばらになりました。グリッドの行の高さは、その行でいちばん高い画像に合わせて 184px と 218.05px になり、低い画像の下には白いすき間が残ります。上の画像の「aspect-ratio なし」が、この並び方です。

object-fit の cover は、画像の縦横比を保ったまま枠全体を埋め、枠に収まらない部分を切り取る指定です。object-fit を書かないと、既定の fill になり、画像は枠に合わせて引き伸ばされます。

画像全体を見せたいときは contain にする

画像の端を切り取りたくないときは、object-fit: contain にします。枠の大きさは cover のときと同じ 4:3 のままで、画像全体が枠の中に収まるように縮小されます。

css
.gallery img {
  /* ほかの指定は同じ */
  object-fit: contain;
  background-color: #eefafd;
}

枠のうち画像で埋まらない部分には、img の背景が見えます。横に長い 8:3 の画像では、枠の上端から 5% の位置の色が、背景に付けた薄い水色の rgb(238, 250, 253) でした。縦長 9:16 の画像では、左端から 5% の位置が同じ薄い水色でした。

切り取る位置は object-position で決める

cover で切り取るとき、どこを残すかは object-position で決めます。既定値は 50% 50% で、画像の真ん中を残し、上下(または左右)の端を同じだけ切り取ります。

縦長 2:3 の画像には、上のほうにピンクの猫の顔を描いています。既定のままだと、4:3 の枠には画像の真ん中しか入らず、猫の顔の中心は枠の上端より上に出て、切り取られました。

上の端を残したいときは、object-position: center top にします。

css
/* 縦長 2:3 の画像(2枚目)だけ */
.gallery li:nth-child(2) img {
  object-position: center top;
}

この指定では、猫の顔の中心は枠の上から3分の1の位置に来て、その画素はピンク(rgb(216, 88, 120))でした。

同じ縦長の画像を、4:3 の枠に cover で入れた画面を2つ並べたもの。左の「center(既定)」では、猫の顔が上の端で切れ、耳が見えない。右の「center top」では、猫の顔が耳まで全部見え、下の「縦長 2:3」の文字が下の端で切れている
object-position を既定のままにした場合と、center top にした場合

位置は、top や left のようなキーワードのほかに、50% 20% のようなパーセントや 10px 0 のような長さでも書けます。画像ごとに残したい場所が違うときは、上のように、その画像だけに object-position を付けます。

height: auto を書き忘れると、属性の高さのままになる

img に width と height の属性を書いているときは、CSS に height: auto が必要です。最初の CSS から height: auto; の1行だけを消すと、画像の高さは、属性に書いた 400px、600px、500px、300px、480px、640px になりました。幅は 122.66px のままなので、枠が縦に細長くなります。getComputedStyle() で見た aspect-ratio は 4 / 3 のままでしたが、形には効いていません。画像の下の文は、ページの上から 1272px の位置まで押し下げられました(height: auto があるときは 216px)。

HTML の仕様では、img の width と height の属性は、CSS の width と height の値として扱われます。CSS で width: 100% を書くと幅は上書きされますが、高さは属性の値が残ります。MDN の aspect-ratio の説明にあるとおり、幅も高さも auto でないボックスでは、aspect-ratio は大きさに影響しません。

読み込み中も、aspect-ratio の枠で場所を取る

aspect-ratio を CSS に書いておくと、画像を読み込む前から、枠の大きさが決まります。画像のファイルを返すのを3秒遅らせて、読み込み中と読み込み後の大きさを比べました。

最初の CSS では、読み込み中も6枚とも幅 122.66px、高さ約 92px で、画像の下の文の位置は、読み込み中も読み込み後も 216px でした。

aspect-ratio を書かず、width と height の属性だけがある場合も、読み込み中から高さが決まっていました。ただし、高さは画像ごとの元の比から決まり、横長 3:2 の画像は 81.77px、縦長 9:16 の画像は 218.05px でした。このとき getComputedStyle() で見た aspect-ratio は auto 600 / 400 のように、属性の値から作られていました。HTML の仕様でも、img の width と height の属性は aspect-ratio の値としても扱われると決められています。MDN の img 要素の説明には、この縦横比を使って、読み込み前に表示に必要な場所を確保すると書かれています。

aspect-ratio も属性もない場合は、読み込み中の画像の高さは 0px でした。画像の下の文は、読み込み中は 32px の位置にあり、読み込んだ後に 434.05px の位置へ下がりました。

「画像を追加する」ボタンにマウスポインターを動かしてクリックする。押した瞬間に「クリック 1回目/1回」がピンクの地になる。上の「aspect-ratio: 4 / 3」では、薄い水色の同じ大きさの枠が6つすぐに並び、下の文の「見出しから 225px」がピンクになる。下の「aspect-ratio なし・属性なし」では、画像の場所が取られず、文は行のすき間の分だけ「見出しから 41px」から「49px」へ下がり、その数字もピンクになる。約1.2秒後に画像が届くと、上の枠には画像が入るだけで文は動かず、下では高さのばらばらな画像が現れて、文が「見出しから 434px」の位置まで下がり、その数字がピンクになる
ボタンで画像を追加したときの様子。画像のファイルは1.2秒遅れて届く。上の枠には、場所が分かるように薄い水色の背景を付けている

属性だけでも、読み込みの前後で文は動きません。ただ、取られる場所は画像ごとの元の比なので、そろえた形で場所を取るには、CSS の aspect-ratio を書きます。

使えるブラウザーのバージョン

MDN の互換性データ(@mdn/browser-compat-data 8.1.8)では、それぞれ次のバージョンから使えます。

プロパティ Chrome Edge Firefox Safari
aspect-ratio 88 88 89 15
object-fit 32 79 36 10
object-position 32 79 36 10

​Posted by 86