CSS aspect-ratio と padding ハック

アスペクト比を定義(設定)できる CSS のプロパティ aspect-ratio と、以前から使われているクロスブラウザ対応のアスペクト比を設定するテクニック padding ハックの使い方について。

作成日:2023年3月27日

aspect-ratio プロパティ

CSS の aspect-ratio プロパティはボックス(矩形の領域)のアスペクト比を設定するプロパティです。

アスペクト比とは、幅(width)と高さ(height)の比率のことで、4:3 や 16:9 などがよく使用されます。

400px 300px アスペクト比 4 : 3 400px 225px アスペクト比 16 : 9

aspect-ratio プロパティを使うとボックスのアスペクト比(幅と高さの比率)を保持することができます。

.aspect-ratio-box-16x9 {
  aspect-ratio: 16/9;  /* アスペクト比の指定  */
  width: 50%;
  background-color: #7aac81;
}
<div class="aspect-ratio-box-16x9"></div>

上記の HTML と CSS で以下のようなボックスが表示されます。

幅と高さを指定していませんが、指定したアスペクト比 16:9 の領域(プレースホルダースペース)が確保されます。ブラウザのウィンドウ幅を変更すると縦横比を保ってボックスのサイズが変化します。

この例の場合、width を 50% に設定しているので、常に親要素の半分の幅でアスペクト比 16:9 のボックスが表示されます。

以下は aspect-ratio: 4/3 を指定しているので、アスペクト比 4:3 のボックスが表示されます。

この例の場合は、height に固定値の 240px を指定しているので、常に高さ 240px、幅 320px(240/3*4)のボックスになります。

.aspect-ratio-box-16x9 {
  aspect-ratio: 4/3;  /* アスペクト比の指定  */
  height: 240px;
  background-color: #ac7a9d;
}

ブラウザの対応状況

現時点(2023/3)で主要なモダンブラウザは aspect-ratio プロパティをサポートしています。

2021年初頭に Chrome と Firefox でサポートされ、2021年末に Safari でサポートされています。IE は対象外です。

https://caniuse.com/

aspect-ratio の指定方法

以下が aspect-ratio の書式です。

aspect-ratio の値に width と height の比率をスラッシュ / で区切って指定します。

スラッシュと値の間にスペースがあってもなくても大丈夫です。

指定方法 1
aspect-ratio: width / height ;

または、スラッシュと height を省略して、width の比率(width を height で割った値 = width/height)のみを指定することもできます(height の値は 1 として計算されます)。

※ 但し、この方法で指定すると、https://validator.w3.org/ では「Error: CSS: aspect-ratio: 1 is not a aspect-ratio value.」のようなエラーになります。

指定方法 2
aspect-ratio: width ;
/* height が 1 に対する width の比率で指定する場合 */

例えば以下のように指定します。

aspect-ratio: 1/1;        /* aspect-ratio : 1 でも同じ */
aspect-ratio: 4/3;        /* aspect-ratio : 1.333333 とほぼ同じ */
aspect-ratio: 400/300;    /* 上記の 4/3 と同じことです */
aspect-ratio: 16/9;       /* aspect-ratio : 1.777777 とほぼ同じ */
aspect-ratio: 1618/1000;  /* aspect-ratio : 1.618 でも同じ */

以下は指定例です。

<div class="box-wrapper">
  <div class="aspect-ratio-1x1 box">1/1</div>
  <div class="aspect-ratio-4x3 box">4/3</div>
  <div class="aspect-ratio-16x9 box">16/9</div>
  <div class="aspect-ratio-gold box">黄金比</div>
</div>
.aspect-ratio-1x1 {
  aspect-ratio: 1/1;
}
.aspect-ratio-4x3 {
  aspect-ratio: 4/3;
}
.aspect-ratio-16x9 {
  aspect-ratio: 16/9;
}
.aspect-ratio-gold {
  aspect-ratio: 1618/1000;
}
.box-wrapper {
  display: flex;
  height: 100px;
}
.box {
  background-color: #999;
  margin-right: 20px;
  color: #fff;
}
1 / 1
4 / 3
16 / 9
黄金比

width と height

aspect-ratio を指定した要素の幅(width や max-width)または高さ(height や max-height)のいずれかを明示的に指定すると、指定されていない方の値は、アスペクト比によって自動的に決定されます。

また、幅と高さの両方を auto 以外に設定すると、アスペクト比は適用されなくなります。

<div class="aspect-box width-box">width: 200px</div>
<div class="aspect-box height-box">height: 100px</div>
<div class="aspect-box width-height-box">width: 200px height: 200px</div>
<div class="aspect-box no-width-no-height">no width & no height </div>
.aspect-box {
  aspect-ratio: 16/9;
}
.width-box {
  width: 200px;  /* width のみを指定 */
  background-color: #4d6fbe;
}
.height-box {
  height: 100px;  /* height のみを指定 */
  background-color: #7aac81;
}
.width-height-box {
  width: 200px;  /* width と height の両方を指定 */
  height: 200px;
  background-color: #bc7a87;
}
.no-width-no-height {
  background-color: #6cb8c9; /* width と height の指定なし */
}

width: 200px を指定した要素の高さはアスペクト比 16:9 により 112.5px(200*9/16)になります。

width: 200px
16 : 9

height: 100px を指定した要素の幅はアスペクト比 16:9 により 177.77px(100*16/9)になります。

height: 100px
16 : 9

width: 200px と height: 200px で幅と高さの両方を指定した要素はアスペクト比は適用されません

width: 200px
height: 200px

aspect-ratio を指定して、幅(width や max-width)と高さ(height や max-height)のいずれの指定もない場合は、親要素の設定及び自身のコンテンツにより異なります。

この例の場合は以下のように親要素いっぱいに広がります。

no width & no height
幅と高さの両方の指定なし
16 : 9

幅と高さのいずれも指定せずに、親要素でサイズを制御することができます。但し、親要素に display: flex や display: grid が指定されている場合(Flexbox や Grid)は注意が必要です。

コンテンツ

aspect-ratio を指定していて、コンテンツがそのそのボックスに収まらない場合、デフォルトではコンテンツに合わせて拡張します。

以下の2つのボックスにはいずれも aspect-ratio: 2/1 と明示的に width を指定しています。

.aspect-box2 {
  aspect-ratio: 2/1;
  width: 33%; /* 幅を指定 */
  background-color: #7aac81;
  color: #ddd;
  padding: 1rem;
}
.box-wrapper2 {
  display: flex;
  align-items: flex-start; /* デフォルトの stretch 以外を指定 */
  gap: 1rem;
}

この例では親要素に display: flex を指定しているため、align-items にデフォルトの stretch 以外を指定しないと、全ての Flex アイテムの高さが Flex コンテナの高さまで拡張されます(flexbox や grid)。

<div class="box-wrapper2">
  <div class="aspect-box2">
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. </p>
  </div>
  <div class="aspect-box2">
    <p>Minima maxime odio nihil quibusdam quae iste iure numquam molestias ratione ea, veritatis...</p>
  </div>
</div>

2つ目のボックスはコンテンツが多いので高さが拡張されています。

Lorem ipsum dolor sit amet consectetur adipisicing elit.

Minima maxime odio nihil quibusdam quae iste iure numquam molestias ratione ea, veritatis dolorem temporibus, unde placeat ipsum earum ad tempore aspernatur vero distinctio consequuntur natus praesentium. Ipsum, soluta ut.

width の代わりに明示的に height を指定するとコンテンツがはみ出して表示されます。

.aspect-box3 {
  aspect-ratio: 2/1;
  height: 100px;  /* 高さを指定 */
  background-color: #7aac81;
  color: #ccc;
  padding: 1rem;
}

Lorem ipsum dolor sit amet consectetur adipisicing elit.

Minima maxime odio nihil quibusdam quae iste iure numquam molestias ratione ea, veritatis dolorem temporibus, unde placeat ipsum earum ad tempore aspernatur vero distinctio consequuntur natus praesentium. Ipsum, soluta ut.

必要に応じて overflow プロパティで非表示にしたり、スクロールバーを表示することができます。

以下は左側のボックスには overflow: hidden を指定してはみ出した部分を非表示にし、右側のボックスには overflow: scroll を指定してスクロールして表示できるようにしています。

Minima maxime odio nihil quibusdam quae iste iure numquam molestias ratione ea, veritatis dolorem temporibus, unde placeat ipsum earum ad tempore aspernatur vero distinctio consequuntur natus praesentium. Ipsum, soluta ut.

Minima maxime odio nihil quibusdam quae iste iure numquam molestias ratione ea, veritatis dolorem temporibus, unde placeat ipsum earum ad tempore aspernatur vero distinctio consequuntur natus praesentium. Ipsum, soluta ut.

Flexbox や Grid

flexbox や grid はデフォルトで高さはコンテンツに合わせて調整されます。

アスペクト比を設定したボックス内に収まるよりも長いコンテンツを持つアイテムが1つあると、 flexbox では縦横比を無視して、最も長い項目の高さに合わせて拡張されます。

<div class="box-wrapper4">
  <div class="aspect-box4">
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. </p>
  </div>
  <div class="aspect-box4">
    <p>Minima maxime odio nihil quibusdam quae iste iure numquam molestias ratione ea, veritatis ...</p>
  </div>
</div>
.box-wrapper4 {
  display: flex;
  align-items: stretch; /* デフォルトの stretch */
  gap: 1rem;
  margin: 40px 0 80px;
}

.aspect-box4 {
  aspect-ratio: 2/1;
  width: 33%;
  background-color: #7aac81;
  color: #ddd;
  padding: 1rem;
}

例えば、上記のように親要素に display: flex を指定して、align-items を stretch に指定(または align-items を省略)した場合、以下のように最も長い項目の高さに合わせて拡張されます。

Lorem ipsum dolor sit amet consectetur adipisicing elit.

Minima maxime odio nihil quibusdam quae iste iure numquam molestias ratione ea, veritatis dolorem temporibus, unde placeat ipsum earum ad tempore aspernatur vero distinctio consequuntur natus praesentium. Ipsum, soluta ut.

以下は Grid の例です。アスペクト比を設定したボックス内に収まる場合はアスペクト比が保たれますが、ボックス内に収まるよりも長いコンテンツの場合は拡張されます。

<div class="box-wrapper5">
  <div class="aspect-box5">
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. </p>
  </div>
  <div class="aspect-box5">
    <p>Minima maxime odio nihil quibusdam quae iste iure numquam molestias ...</p>
  </div>
</div>

grid-template-rows や grid-auto-rows に指定する値によって異なります。

.box-wrapper5 {
  display: grid;  /* grid を指定 */
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: auto;
  gap: 1rem;
}

.aspect-box5 {
  aspect-ratio: 2/1;
  width: 100%;
  background-color: #7aac81;
  color: #ddd;
  padding: 1rem;
}

Lorem ipsum dolor sit amet consectetur adipisicing elit.

Minima maxime odio nihil quibusdam quae iste iure numquam molestias ratione ea, veritatis dolorem temporibus, unde placeat ipsum earum ad tempore aspernatur vero distinctio consequuntur natus praesentium. Ipsum, soluta ut.

画像の配置(表示)

縦横比を指定して画像を表示する場合、aspect-ratio と object-fit を使うと簡単です。

以下は aspect-ratio を指定した div 要素に img 要素を配置してアスペクト比にフィットするように画像を表示する例です。

<div class="aspect-img-box">
  <img src="../images/sample.jpg" alt="lotus">
</div>

親要素にフィットさせる場合、object-fit を指定するフィットさせたい要素には明示的に幅と高さを設定する必要があります。

この例では、object-fit に cover を指定しているので画像は縦横比を維持したまま、要素のコンテンツボックスに収まるように拡大縮小されます(はみ出してしまう部分はトリミングされます)。

.aspect-img-box {
  aspect-ratio: 16/9;
  max-width: 300px;
}

.aspect-img-box img {
  display: block;
  width: 100%;  /* 明示的に幅を設定(必須) */
  height: 100%; /* 明示的に高さを設定(必須) */
  object-fit: cover; /* コンテンツボックスに収まるように拡大縮小 */
}
lotus

ロゴ画像など全体を表示する必要がある(トリムしない)場合は object-fit に cover を指定します。

img 要素に aspect-ratio を設定

img 要素自体に aspect-ratio でアスペクト比を指定して表示することもできます。

<img class="aspect-img" src="../images/sample.jpg" alt="lotus">

この場合、画像には幅(width や max-width)または高さ(height や max-height)のいずれかを指定します。width: 100% を指定すれば親要素の幅により伸縮されます。

img.aspect-img {
  aspect-ratio: 1618/1000;
  object-fit: cover;
  display: block;
  width: 100%;
  height: auto;  /* img 要素に height 属性が指定されている場合は設定 */
  max-width: 300px; /* 必要に応じて */
}

lotus

背景画像

以下は aspect-ratio を指定した div 要素に背景画像として表示する例です。

背景画像として表示する場合は、object-fit の代わりに background-size で表示するサイズを指定します。

また、必要に応じて background-position で位置を調整することができます。この例では花を中心に表示するために指定しています。

<div class="aspect-img-bg"></div>
.aspect-img-bg {
  aspect-ratio: 1/1;
  max-width: 200px;
  background-image: url(../images/css/cp-01.jpg);
  background-repeat: no-repeat;
  background-size: cover;
  background-position: 30% 50%;
}

絶対配置

aspect-ratio を指定した div 要素に img 要素を絶対配置する例です。div 要素に position: relative を指定し、img 要素に position: absolute を指定するだけで、最初の例の通常の配置と変わりません。

この例では、画像の他に div 要素も絶対配置して、マウスオーバーするとスライドアップする transition アニメーションを設定しています。

<div class="aspect-parent">
  <img src="../images/sample.jpg" alt="lotus">
  <div class="cover"></div>
</div>

スライドアップする div 要素(.cover )は、top, bottom, left, right に 0 を指定して、aspect-ratio を指定した div 要素いっぱいに広がるようにしていますが、代わりに画像同様、 width: 100% と height: 100% を指定しても同じ結果になります(画像は object-fit を使うので、width と height で指定する必要があります)。

.aspect-parent {
  aspect-ratio: 2/1;
  position: relative; /* 絶対配置の基準 */
  width: 40%;
  overflow: hidden;
}
.aspect-parent img {
  position: absolute; /* 絶対配置 */
  width: 100%;  /* 明示的に幅を設定(必須) */
  height: 100%; /* 明示的に高さを設定(必須) */
  object-fit: cover; /* コンテンツボックスに収まるように拡大縮小 */
}
.cover {
  position: absolute; /* 絶対配置 */
  top:0;
  bottom: 0;
  left: 0;
  right: 0;
  /* または
  width: 100%;
  height: 100%; */
  background-color: #7aac81;
  opacity: 1;
  transition: transform .3s, opacity .5s;
}
.aspect-parent:hover .cover {
  transform: translateY(-100%);
  opacity: 0;
}
lotus

Hover Me!

flexbox で配置

以下はアスペクト比を指定して flexbox でブラウザの幅が920px以下の場合は2列で、920pxより大きい場合は3列で画像を表示する例です。

<div class="fbox-images">
  <img src="../images/sample01.jpg" alt="lotus">
  <img src="../images/sample02.jpg" alt="hydrangea">
  <img src="../images/sample03.jpg" alt="hydrangea">
</div>

920pxより大きい場合は3列で表示するので、画像の幅を calc(33.3% - 4px) とし(4px は gap)、920px以下の場合は2列で表示するようにメディアクエリで calc(50% - 4px) としています。

.fbox-images {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  max-width: 740px;
}

.fbox-images img {
  aspect-ratio: 4/3;
  object-fit: cover;
  display: block;
  width: calc(33.3% - 4px);
}

@media (max-width: 920px) {
  .fbox-images img {
    width: calc(50% - 4px);
  }
}
lotus hydrangea hydrangea
flex-shrink や align-items

以下は flexbox で左側にアスペクト比を指定した div 要素内に画像を配置し、右側に可変のテキストを配置する例です。

<div class="media">
  <div class="media-img">
    <img src="../images/sample.jpg" alt="hydrangea">
  </div>
  <div class="media-content">
    <h3 class="media-title">align-items: stretch</h3>
    <p>Lorem ipsum dolor sit amet consectetur adipisicing elit....</p>
    <p>Magnam, repellat. Voluptatem sapiente hic distinctio..</p>
  </div>
</div>

この例では、画像の親要素にアスペクト比を設定して、width: 30% を指定しています。

また、flex-shrink: 0 を指定しています。

flex-shrink は Flex アイテム全体の幅が Flex コンテナの主軸の幅より大きい場合に縮む比率を数値で指定するプロパティですが、デフォルトは 1 なので自動的に縮小されてしまうので 0 を指定して縮まずにオリジナルサイズを維持するようにしています。

flex ショートハンドを使って flex: 0 0 auto または flex: 0 0 30% としても同じです(2番めの値が flex-shrink の値)。.media-content はデフォルト(flex: 0 1 auto)を使用しているので省略しています。

.media {
  display: flex;
  align-items: stretch;  /* デフォルトの strech */
  max-width: 740px;
  border: 1px solid #ddd;
}
.media .media-img {
  aspect-ratio: 1/1;
  margin: .5rem 1rem .5rem .5rem;
  width: 30%;
  max-width: 300px;
  flex-shrink: 0;  /* 縮まずにオリジナルサイズを維持(または flex: 0 0 auto) */
}
.media .media-img img {
  width: 100%;  /* 親要素いっぱいに */
  height: 100%;  /* 親要素いっぱいに */
  object-fit: cover;;  /* コンテンツボックスに収まるように拡大縮小 */
}
.media .media-title {
  font-size: 1.5em;
  padding: 1rem 0;
}

align-items

この例の場合、align-items にデフォルトの stretch を指定しているので、右側のテキストがアスペクト比を設定したボックス内に収まるよりも長い場合、コンテンツに合わせて高さが拡張します。

このため、ブラウザの幅を狭めると、左の画像の高さは拡張されます(flexbox や grid)。

関連項目:デフォルトの動作を利用

hydrangea

align-items: stretch

Lorem ipsum dolor sit amet consectetur adipisicing elit. Assumenda consectetur quos quis, ipsam accusantium fugiat sunt quam accusamus laboriosam ullam necessitatibus odio nam voluptatibus. Perferendis eligendi sit modi deserunt dicta.

Magnam, repellat. Voluptatem sapiente hic distinctio maxime laboriosam, delectus cumque suscipit! Consequuntur sunt unde saepe voluptatem commodi illum facilis doloribus sed.

画像の高さ(アスペクト比)を保ちたい場合は、align-items にデフォルトの stretch 以外を指定します。

以下は、align-items に center を指定して、右側のテキストがアスペクト比のボックスに収まらない場合でも、画像を拡張しないようにする例です。

.media2 {
  display: flex;
  align-items: center;  /* center を指定(その他は前述の例と同じ) */
  max-width: 740px;
  margin: 60px 0;
  border: 1px solid #ddd;
}

.media2 .media-img {
  aspect-ratio: 1/1;
  margin: .5rem 1rem .5rem .5rem;
  width: 30%;
  max-width: 300px;
  flex-shrink: 0;
}

.media2 .media-img img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.media2 .media-title {
  font-size: 1.5em;
  padding: 1rem 0;
}
hydrangea

align-items: center

Lorem ipsum dolor sit amet consectetur adipisicing elit. Assumenda consectetur quos quis, ipsam accusantium fugiat sunt quam accusamus laboriosam ullam necessitatibus odio nam voluptatibus. Perferendis eligendi sit modi deserunt dicta.

Magnam, repellat. Voluptatem sapiente hic distinctio maxime laboriosam, delectus cumque suscipit! Consequuntur sunt unde saepe voluptatem commodi illum facilis doloribus sed.

aspect-ratio の使用例

縦横比を保持するために今までは padding ハックを使用していた場面で、aspect-ratio を使うと簡潔に記述することができます。

以下はカード型のコンテンツの画像を揃える例です。1200 x 741(1.618:1), 1200 x 800(4:3), 1200 x 674(16:9)のアスペクト比が異なる画像を使っています。

異なるアスペクト比の画像でも aspect-ratio と object-fit を使うことで簡単に揃えることができます。

painting on the window

Aspernatur, accusantium reprehenderit tenetur, sapiente voluptatum non aliquid, possimus quam modi nulla quos! Cumque veritatis consectetur ipsa deserunt, laborum sed culpa? Iure voluptate reiciendis quam saepe deserunt molestiae esse. Totam?

Manhattan sunset

Commodi ducimus nisi incidunt eos debitis alias ullam, expedita doloribus eveniet tenetur soluta earum reiciendis mollitia culpa facilis, veniam quod voluptate est quis facere architecto! Voluptatibus porro officia dolores totam.

NY drug store

Lorem ipsum dolor, sit amet consectetur adipisicing elit. Aut neque fugiat libero nisi reiciendis ipsam animi ipsa incidunt. Temporibus est, nemo sint quibusdam asperiores at? Vel modi debitis architecto corporis.

HTML
<div class="flex">
  <div class="card">
    <img src="../images/golden.jpg" alt="painting on the window">
    <p>Aspernatur, accusantium reprehenderit tenetur,...</p>
  </div>
  <div class="card">
    <img src="../images/4x3.jpg" alt="Manhattan sunset">
    <p>Commodi ducimus nisi incidunt eos debitis alias ....</p>
  </div>
  <div class="card">
    <img src="../images/16x9.jpg" alt="NY drug store">
    <p>Lorem ipsum dolor, sit amet consectetur adipisicing elit...</p>
  </div>
</div>
CSS
.flex {
  display: flex;
}
.card {
  flex: 0 0 33%;  /* 基準幅を 33% として3列で表示(伸縮なし) */
  border: 1px solid #ccc;
  margin: 0 10px;
}
.card img {
  object-fit: cover;    /* cover を指定 */
  aspect-ratio: 1618/1000;   /* アスペクト比を指定 */
  width: 100%;
}
.card p {
  padding: 10px;
}

object-fit の値に cover を指定しているので、aspect-ratio で指定したアスペクト比と異なる画像はトリミングされて表示されることになります。

以下は aspect-ratio の値を 1/1 に指定した場合です(その他は同じ)。

.card img {
  object-fit: cover;
  aspect-ratio: 1/1; /* アスペクト比を 1/1 */
  width: 100%;
}
painting on the window

Aspernatur, accusantium reprehenderit tenetur, sapiente voluptatum non aliquid, possimus quam modi nulla quos! Cumque veritatis consectetur ipsa deserunt, laborum sed culpa? Iure voluptate reiciendis quam saepe deserunt molestiae esse. Totam?

Manhattan sunset

Commodi ducimus nisi incidunt eos debitis alias ullam, expedita doloribus eveniet tenetur soluta earum reiciendis mollitia culpa facilis, veniam quod voluptate est quis facere architecto! Voluptatibus porro officia dolores totam.

NY drug store

Lorem ipsum dolor, sit amet consectetur adipisicing elit. Aut neque fugiat libero nisi reiciendis ipsam animi ipsa incidunt. Temporibus est, nemo sint quibusdam asperiores at? Vel modi debitis architecto corporis.

以下は、object-fitcontain を指定して全体を表示する例です。

この例では object-position を使って画像の位置を上端に配置しています。

アスペクト比が異なる画像では、全体を表示するためにトリミングされて余白がでます。※ わかりやすいように赤い背景色を指定しています。

.card img {
  object-fit: contain;   /* contain を指定 */
  aspect-ratio: 1618/1000;
  object-position: center top;   /* 画像の位置を上端に配置 */
  background-color:red;  /* 背景色を指定 */
  width: 100%;
}
painting on the window

Aspernatur, accusantium reprehenderit tenetur, sapiente voluptatum non aliquid, possimus quam modi nulla quos! Cumque veritatis consectetur ipsa deserunt, laborum sed culpa? Iure voluptate reiciendis quam saepe deserunt molestiae esse. Totam?

Manhattan sunset

Commodi ducimus nisi incidunt eos debitis alias ullam, expedita doloribus eveniet tenetur soluta earum reiciendis mollitia culpa facilis, veniam quod voluptate est quis facere architecto! Voluptatibus porro officia dolores totam.

NY drug store

Lorem ipsum dolor, sit amet consectetur adipisicing elit. Aut neque fugiat libero nisi reiciendis ipsam animi ipsa incidunt. Temporibus est, nemo sint quibusdam asperiores at? Vel modi debitis architecto corporis.

関連項目:object-fit

デフォルトの動作を利用

aspect-ratio を指定していて、コンテンツがそのそのボックスに収まらない場合、デフォルトではコンテンツに合わせて拡張します。

例えば、片側に画像があり、反対側にテキストがあるコンポーネントの場合、画像(またはその親要素)にアスペクト比を設定すると、テキスト列は少なくとも画像の高さと一致する高さになります。 ただし、テキストが長い場合、画像はテキストの高さに合わせて拡大されます。

以下はこのデフォルトの動作を利用した例です。

.cta-wrapper {
  margin: 60px 0;
  max-width: 740px;
}

.cta {
  display: flex;
  border: 1px solid #ccc;
  margin-bottom: 2rem;
}

.cta-img {
  aspect-ratio: 16/9;    /* contain を指定 */
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: 30%;
  margin: 0 20px 0 0;
}

.cta-img img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;   /* 親要素のアスペクト比にフィットするように */
}

.cta-img img.left30 {
  object-position: 30%;  /* 画像の位置の調整用 */
}

.cta-img img.left45 {
  object-position: 45%;  /* 画像の位置の調整用 */
}

.cta .cta-content {
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: calc(70% - 20px);
  padding: 1rem;
}

.cta-content h3 {
  margin: 0 0 1rem 0;
  padding: 0;
}
<div class="cta-wrapper">
  <article class="cta">
    <div class="cta-img">
      <img class="left45" src="../images/golden.jpg" alt="painting on the window">
    </div>
    <div class="cta-content">
      <h3>Lorem ipsum</h3>
      <p>Lorem ipsum dolor sit amet consectetur adipisicing ....!</p>
      <p>Aut ut dolores unde voluptate consequuntur saepe ...</p>
      <p><a href="#">Read all</a></p>
    </div>
  </article>
  <article class="cta">
    <div class="cta-img">
      <img src="../images/4x3.jpg" alt="Manhattan sunset">
    </div>
    <div class="cta-content">
      <h3>Dolores nemo</h3>
      <p>Dolores nemo possimus quam ex rerum nulla est cupiditate ...! </p>
      <p><a href="#">Read all</a></p>
    </div>
  </article>
  <article class="cta">
    <div class="cta-img">
      <img class="left30" src="../images/16x9.jpg" alt="NY drug storew">
    </div>
    <div class="cta-content">
      <h3>Aperiam nam dignissimos</h3>
      <p>Aperiam nam dignissimos consectetur, illo ad omnis ...</p>
      <p><a href="#">Read all</a></p>
    </div>
  </article>
</div>

この例ではすべて横長の画像を使用していますが、テキストが長い場合は縦長の画像を用意するなどが考えられます。

painting on the window

Lorem ipsum

Lorem ipsum dolor sit amet consectetur adipisicing elit. A animi esse corporis aspernatur id doloribus tenetur veniam reprehenderit quia, officia, ipsa sequi impedit. Error possimus, ex a aliquam illum tempora!

Aut ut dolores unde voluptate consequuntur saepe tempore velit ratione perspiciatis perferendis reprehenderit sequi illo, pariatur autem. Labore deserunt tempore corrupti consectetur? Error culpa reiciendis officiis totam accusamus at tempore?

Read all

Manhattan sunset

Dolores nemo

Dolores nemo possimus quam ex rerum nulla est cupiditate natus consectetur blanditiis!

Read all

NY drug storew

Aperiam nam dignissimos

Aperiam nam dignissimos consectetur, illo ad omnis voluptates similique amet eum aliquid minus sapiente sequi beatae officia repellat, officiis laborum totam. Quaerat quos tenetur, veritatis fugiat blanditiis incidunt velit praesentium!

Read all

上記の場合、ブラウザの幅を変化させると、画像とコンテンツの幅も伸縮させるようにしていますが、以下のようにすればコンテンツの幅は伸縮させ、画像の幅を固定にします。

変更部分
.cta-img {
  aspect-ratio: 16/9;
  flex-grow: 0;
  flex-shrink: 0;
  flex-basis: 180px;
  margin: 0 20px 0 0;
}

/* flex ショートハンドで記述する場合
.cta-img {
  aspect-ratio: 16/9;
  flex: 0 0 180px;
  margin: 0 20px 0 0;
}
*/

.cta .cta-content {
  flex-grow: 1;
  flex-shrink: 1;
  flex-basis: auto;
  padding: 1rem;
}

/* flex ショートハンドで記述する場合
.cta .cta-content {
  flex: 1 1 auto;
  padding: 1rem;
}
*/

Googel Map をレスポンシブ表示

Googel Map のレスポンシブ表示の例です。関連ページ:API キーなしでサイトに Google マップを表示

以下の例では Googel Map の ifram 要素をラッパー要素で囲んで、最大幅やボーダーなどを設定しています(ラッパー要素で囲むのは必須ではありません)。

HTML
<div class="gmap">
  <!-- Googel Map の埋め込みコード -->
  <iframe src="https://www.google.com/maps/embed?pb=!1m14!1m8!1m3!1d3240.88852697053!2d139.8059981!3d35.6797467!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x6018891bfc592aad%3A0xd222029e041bd31b!2z44CSMTM1LTAwMjIg5p2x5Lqs6YO95rGf5p2x5Yy65LiJ5aW977yU5LiB55uu77yR4oiS77yRIOadseS6rOmDveePvuS7o-e-juihk-mkqA!5e0!3m2!1sja!2sjp!4v1621326065459!5m2!1sja!2sjp" width="500" height="250" style="border:0;" allowfullscreen="" loading="lazy"></iframe>
</div>

ラッパー要素では、margin や max-width、border などを指定しています。Googel Map の iframe 要素に aspect-ratio でアスペクト比を設定し、width と height に 100% を指定します。

.gmap {
  margin: 50px 0px;
  max-width: 500px;
  padding: 4px;
  border: 1px solid #CCC;
}
.gmap iframe {
  aspect-ratio: 2/1;  /* 好みのアスペクト比を指定 */
  height: 100%;       /* height に 100% を指定 */
  width: 100%;        /* width に 100% を指定 */
}
Padding ハック

以下は前述の aspect-ratio を使ったのとほぼ同じことを Padding ハックで実装する例です。

前述の例の HTML と比べると、外側に更にラッパー要素を追加しています。

<div class="map-wrapper">
  <div class="googlemap">
    <iframe src="https://www.google.com/maps/embed?pb=!1m14!1m8!1m3!1d3240.88852697053!2d139.8059981!3d35.6797467!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x6018891bfc592aad%3A0xd222029e041bd31b!2z44CSMTM1LTAwMjIg5p2x5Lqs6YO95rGf5p2x5Yy65LiJ5aW977yU5LiB55uu77yR4oiS77yRIOadseS6rOmDveePvuS7o-e-juihk-mkqA!5e0!3m2!1sja!2sjp!4v1621326065459!5m2!1sja!2sjp" width="800" height="450" style="border:0;" allowfullscreen="" loading="lazy"></iframe>
  </div>
</div>

Padding ハックでは親要素のパディング領域に Google Map を表示します。そのため、親要素に position: relative を指定して、子要素(iframe)の基準とし、パディング領域に対してアスペクト比を設定します。子要素には絶対配置を指定し、親要素のパディング領域に配置します。

この例では padding-bottom: 56.25% でアスペクト比を 16:9 にしています。padding-bottom の代わりに padding-top を指定しても同じです。

.map-wrapper {
  max-width: 500px;
  margin: 20px 0;
  padding: 4px;
  border: 1px solid #CCC;
}
.googlemap {
  position: relative;  /* 子要素(iframe)の基準とする */
  padding-bottom: 56.25%; /* 縦横比を 16:9(幅の 56.25% を高さとする) */
  height: 0;
  overflow: hidden;
}
.googlemap iframe {
  position: absolute; /* 親要素のパディング領域に配置するために、絶対配置を指定 */
  top: 0;
  left: 0;
  width: 100%; /* 親コンテナの幅いっぱいに表示 */
  height: 100%; /* 親コンテナの高さいっぱいに表示 */
}

関連ページ:API キーなしでサイトに Google マップを表示

Youtube をレスポンシブ表示

Youtube も Googel Map と同様、aspect-ratio を使って簡単にレスポンシブ表示することができます。

<div class="yt-wrapper">
  <!-- YouTubeの埋め込みコード -->
  <iframe width="500" height="375" src="https://www.youtube.com/embed/dH3GSrCmzC8" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
</div>

Youtube の埋め込みコードの iframe 要素に aspect-ratio を指定し、width と height に 100% を指定します。

.yt-wrapper {
  max-width: 500px;   /* 最大幅を指定 */
}
.yt-wrapper iframe {
  aspect-ratio: 4/3;  /* 好みのアスペクト比を指定 */
  height: 100%;       /* height に 100% を指定 */
  width: 100%;        /* width に 100% を指定 */
}

Youtube 関連ページ

Padding ハック

以下は Padding ハックを使ってレスポンシブ表示する例です。

<div class="youtube_wrapper">
  <div class="youtube">
    <!-- YouTubeの埋め込みコード -->
    <iframe width="600" height="450" src="https://www.youtube.com/embed/dH3GSrCmzC8" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
  </div>
</div>
.youtube_wrapper {
  max-width: 500px;;   /* 最大幅を指定 */
}
.youtube {
  position: relative; /* 子要素の基準とする */
  padding-bottom: 56.25%; /* 幅の 56.25% を高さとする (16:9) */
  padding-top: 25px; /* 高さの調整(必要に応じて) */
  height: 0;
  overflow: hidden;
}
.youtube iframe {
  position: absolute; /* 親要素のパディング領域に配置するために、絶対配置を指定 */
  top: 0;
  left: 0;
  width: 100%; /* 親コンテナの幅いっぱいに表示 */
  height: 100%; /* 親コンテナの高さいっぱいに表示 */
}

Padding ハック

画像の幅に基づいてアスペクトを保つテクニックとして Padding ハックがあります(padding-top ハックや padding-bottom ハックなどとも呼ばれます)。

要素の padding をパーセンテージ(%)で指定した場合、包含ブロック(親要素)のに対するパーセンテージになるので、Padding ハックはこれを利用してアスペクト比を設定します。

要素の height をパーセンテージ(%)で指定した場合、その要素の高さは包含ブロック(親要素)の高さに対するパーセンテージになります。

<div class="parent1">
  <div class="child1"></div>
</div>
.parent1 {
  width: 300px;
  height: 200px;
  background-color: #f5f8c8;
  border: 1px dashed #999;
}

.child1 {
  width: 100px;
  height: 75%;  /* 親要素 .parent1 の高さの 75%(200px * 0.75 = 150px)*/
  background-color: #7aac81;
}

.child1 の高さ(height)は 親要素 .parent1 の高さ(200px)の 75% なので 150px になります。

100px 150px (200 * 0.75) 親要素の高さに対する % 200px .child1 .parent1 300px

自身の幅を基準に高さをパーセンテージ(比率や割合)で指定できれば良いのですが、aspect-ratio 以外にそのような方法がないため、padding を使って親要素の幅を基準に高さを指定することでアスペクト比を設定します。

padding ハックでは以下のような構造を作ることでアスペクト比を設定します。

一番外側の要素(.parent2-wrapper)は全体の幅を設定するために追加しています。親要素いっぱいにボックス(.child2)を表示するのであれば不要です。

ボックス(.child2)の親要素(.parent2)は、アスペクト比を設定するための枠組みのような存在で、自身のコンテンツの高さは 0 です。但し、padding-top: 56.25% により、 16:9 のアスペクト比のパディング領域が確保され、この部分いっぱいに絶対配置で子要素のボックス(.child2)が表示されます。

ボックス(.child2)は、親要素(.parent2)のパディング領域いっぱいに配置するため、position: absolute を設定し、幅と高さを 100% にします。

<div class="parent2-wrapper">
  <div class="parent2">
    <div class="child2"></div> <!-- アスペクト比が適用される -->
  </div>
</div>

アスペクト比は padding-top または padding-bottom に height ÷ width x 100 で算出した値を % で指定します。例えば、4:3 の場合は 3 ÷ 4 x 100 で 75% になります。

必要に応じて、.parent2-wrapper または .parent2 に overflow: hidden を指定します。例えば、.child2 に長いテキストを入れると、overflow: hidden を指定していない場合、はみ出してしまいます。

.parent2-wrapper {
  width: 50%;    /* 幅を指定(任意の値) */
  overflow: hidden;    /* 必要に応じて */
}

.parent2 {
  position: relative;    /* 子要素(.child2)を配置する基準とする */
  padding-top: 56.25%;   /* 16:9 のアスペクト比(9 / 16 * 100 %) */
}

.child2 {
  position: absolute;    /* 絶対配置 */
  top: 0;
  left: 0;
  width: 100%;    /* 幅 100% */
  height: 100%;    /* 高さ 100% */
  background-color: #7aac81;
}

上記により以下のような幅 50% でアスペクト比が 16:9 のボックスが表示されます。

aspect-ratio プロパティを使えば、上記と同じことを以下のように簡潔に記述できます。

<div class="ar-box-16x9"></div>

overflow: hidden を指定しないと、コンテンツに長いテキストが入っている場合などではアスペクト比を保つことができません。

.ar-box-16x9 {
  aspect-ratio: 16 / 9;
  width: 50%;
  background-color: #4d6fbe;
  overflow: hidden;
}

::before 擬似要素を使う

前述の Padding ハックでは3つの div 要素を使用しましたが、::before 擬似要素を使えば2つの div 要素で構造を作成できます。

<div class="parent3">
  <div class="child3"></div>
</div>

::before と content プロパティを使うと、対象の要素のコンテンツの前に擬似要素を挿入(生成)することができます。このハックでは親要素(.parent3)に ::before 擬似要素を使ってアスペクト比を確保するためのパディング領域を挿入します。

::before の content には空の文字列を指定してコンテンツとしては何も挿入していませんが、パディングの領域を設定しています。

挿入される疑似要素はデフォルトでは inline なので、幅や高さを持つことができるように display: block を指定しています(これで挿入される疑似要素は .parent3 と同じ幅を持ちます)。

そして挿入した疑似要素に padding-top を設定してアスペクト比の領域を作成しています。

挿入された疑似要素 .parent3::before は、前述のハックの .parent2 に相当します。但し、.child3 を配置する基準は .parent3 として position: relative を指定しています。

.parent3 {
  position: relative;    /* 子要素(.child3)を配置する基準とする */
  width: 50%;
  overflow: hidden;
}

.parent3::before {
  content: "";  /* 空文字列 */
  display: block;   /* display: block でブロック要素に */
  padding-top: 56.25%;    /* 16:9 のアスペクト比 */
}

.child3 {
  position: absolute; /* 絶対配置 */
  top: 0;
  left: 0;
  width: 100%;  /* 幅 100% */
  height: 100%;  /* 高さ 100% */
  background-color: #7aac81;
}

以下は Chrome のデベロッパーツールで確認した .parent3::before と .child3 です。

使用例

以下は aspect-ratio の使用例と同じカード型のコンテンツの画像を揃える例です。

aspect-ratio の使用例の場合は、img 要素に直接 aspect-ratio でアスペクト比を指定しますが、Padding ハックの場合は img 要素をラップする要素を追加する必要があります。

<div class="flex">
  <div class="card2">
    <div class="img-wrapper">
      <img src="../images/golden.jpg" alt="painting on the window">
    </div>
    <p>Aspernatur, accusantium reprehenderit tenetur...</p>
  </div>
  <div class="card2">
    <div class="img-wrapper">
      <img src="../images/4x3.jpg" alt="Manhattan sunset">
    </div>
    <p>Commodi ducimus nisi incidunt eos debitis alias ...</p>
  </div>
  <div class="card2">
    <div class="img-wrapper">
      <img src="../images/16x9.jpg" alt="NY drug store">
    </div>
    <p>Lorem ipsum dolor, sit amet consectetur adipisicing elit...</p>
  </div>
</div>

img 要素をラップする要素(.img-wrapper)を img 要素を配置する基準とするため position: relative を指定し、::before でパディング領域を作成します。

img 要素のアスペクト比は、.img-wrapper::before の padding-top の値で設定します。

.card2 {
  flex: 0 0 33%; /* 基準幅を 33% として3列で表示(伸縮なし) */
  border: 1px solid #ccc;
  margin: 0 10px;
}

.img-wrapper {
  position: relative;  /* img 要素を配置する基準とする */
}

.img-wrapper::before {
  content: "";
  display: block;  /* display: block でブロック要素に(inline 以外を指定) */
  padding-top: 56.25%;  /* 16:9 のアスペクト比 */
}

.card2 img {
  object-fit: cover;
  position: absolute;   /* 絶対配置 */
  top: 0;
  left: 0;
  width: 100%;  /* 幅 100% */
  height: 100%;  /* 高さ 100% */
}

.card2 p {
  padding: 10px;
}
painting on the window

Aspernatur, accusantium reprehenderit tenetur, sapiente voluptatum non aliquid, possimus quam modi nulla quos! Cumque veritatis consectetur ipsa deserunt, laborum sed culpa? Iure voluptate reiciendis quam saepe deserunt molestiae esse. Totam?

Manhattan sunset

Commodi ducimus nisi incidunt eos debitis alias ullam, expedita doloribus eveniet tenetur soluta earum reiciendis mollitia culpa facilis, veniam quod voluptate est quis facere architecto! Voluptatibus porro officia dolores totam.

NY drug store

Lorem ipsum dolor, sit amet consectetur adipisicing elit. Aut neque fugiat libero nisi reiciendis ipsam animi ipsa incidunt. Temporibus est, nemo sint quibusdam asperiores at? Vel modi debitis architecto corporis.

レイアウトシフトを防止

aspect-ratio プロパティを設定することで、指定したアスペクト比の領域が確保されるので、レイアウトシフト(Cumulative Layout Shift)を防止することができます。

aspect-ratio プロパティを使ってアスペクト比を指定して、width を指定します。必要に応じて(コンテンツに応じて)overflow プロパティ(hidden や scroll)を指定します。

.aspect-ratio-box-16x9 {
  aspect-ratio: 16 / 9;  /* アスペクト比を指定  */
  width: 100%;
  overflow: hidden;  /* ブロック(ボックス)に収まらない場合の指定  */
}

aspect-ratio はほとんどの要素で機能します。以下は img 要素に適用する例です。以下を指定すると、画像がドキュメントに表示されると同時にコンテンツ用のスペースが確保されるので、レイアウトシフトを防止することができます。

img {
  aspect-ratio: 16 / 9;
  width: 100%;
  height: auto;  /* img 要素に(with 属性と)height 属性が指定してある場合は指定 */
}
width と height によるレイアウトシフトの防止

aspect-ratio を使わなくても、画像やビデオ要素にサイズ(width 属性と height 属性)をピクセル値で設定することで、レイアウトシフトを防止することができます。

width 属性と height 属性の値は画像の実際のサイズを指定すれば問題ありません(単位は付けません)。

縦横比が示せれば良いので、以下の場合、width="64" height="36" でも問題ありませんが、CSS でサイズを指定しないと、width 属性と height 属性で指定されたサイズで表示されます。

<!-- width 属性と height 属性を設定(単位は付けない) -->
<img class="foo" src="foo.jpg" width="640" height="360" alt="foo の写真">

実際に表示する幅を CSS で設定します。その際、高さは自動的に調整されるように height に auto を指定します(指定しないと height 属性の値が適用されます)。

img {
  width: 100%;
  height: auto;  /* 自動的に調整されるように auto を設定  */
}

img.foo {
  width: 80%;
  max-width: 600px;
}

2019年〜2020年頃から主要なブラウザでは、img や video 要素に width と height を指定することで、それらの値を基にアスペクト比を算出してスペースを確保するようになっています。