Skip to content

CSS レイアウト (flex-grow, flex-shrink, flex-basis)

ut.code(); Learn「CSS によるレイアウト」に入りきらなかった内容として、flex-growflex-shrinkflex-basis について扱います。

次のような料金プランのカードがあります。

料金プランのカード

HTML のコード
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>料金プラン</title>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<div id="container">
<div class="card card-free">
<div class="plan">Free</div>
<div class="price">無料</div>
<button type="button" class="button">今すぐ始める</button>
<ul>
<li>基本的な機能</li>
</ul>
</div>
<div class="card card-basic">
<div class="plan">Basic</div>
<div class="price">$5</div>
<button type="button" class="button">今すぐ始める</button>
<ul>
<li>基本的な機能</li>
<li>AIアシスタントのサポート</li>
<li>5GBのストレージ</li>
</ul>
</div>
<div class="card card-premium">
<div class="plan">Premium</div>
<div class="price">$20</div>
<button type="button" class="button">今すぐ始める</button>
<ul>
<li>全ての機能</li>
<li>AIアシスタントのサポート</li>
<li>20GBのストレージ</li>
</ul>
</div>
</div>
</body>
</html>
CSS のコード
body {
background-color: #f0f0f0;
}
#container {
display: flex;
gap: 16px;
justify-content: center;
}
.card {
background-color: #fff;
width: 240px;
border: 1px solid #bbb;
border-radius: 8px;
padding: 24px;
}
.plan {
font-weight: bold;
font-size: 24px;
}
.price {
font-size: 20px;
margin-top: 8px;
}
.button {
background-color: #0d6efd;
color: #fff;
font-size: 16px;
width: 100%;
border: none;
border-radius: 8px;
margin-top: 16px;
padding: 8px;
}

現在のところ、画面の幅を変えたときは次のような動作になっています。

しかし、少しぎこちないですね。願わくは、次のように画面幅に合わせてカードが伸縮するようになってほしいものです。

「画面幅に合わせてカードを伸縮させる」ためにはどうすればいいでしょうか。

現状では、親要素である #container の幅が余っていても、子要素である .card の幅は小さいままになっています。

コンテナの幅が余っている様子

親要素の余ったスペースが子要素に分配されれば、カードは画面幅に合わせた大きさになるでしょう。このように、「コンテナ (親要素) の余っているスペースをアイテム (子要素) に分配する」際の挙動をコントロールするのが flex-growflex-shrinkflex-basis という 3 つのプロパティです。

flex-grow プロパティの値を 1 に設定してみましょう。

.card-free {
flex-grow: 1;
}
.card-basic {
flex-grow: 1;
}
.card-premium {
flex-grow: 1;
}

カードを広げることができました。

flex-grow で指定するのは、コンテナの余っているスペースがアイテム (自分) に分配される比率です。つまりここでは、Free、Basic、Premium のそれぞれのカードが、1:1:1 で余っているスペースを分配し合う、という意味になります。

flex-growを1:1:1にした際の図

これは他のアイテムに対する比率です。なので、すべてのカードに同じ正の値を指定するなら、それが flex-grow: 1 だろうが flex-grow: 100 だろうが flex-grow: 0.1 だろうが同じです。1:1:1 が 100:100:100 や 0.1:0.1:0.1 になるだけだからです。

では、今度はそれぞれ 012 を指定してみましょう。

.card-free {
flex-grow: 0;
}
.card-basic {
flex-grow: 1;
}
.card-premium {
flex-grow: 2;
}

Free のカードには余ったスペースが分配されず、Basic と Premium のカードにはそれぞれ 1:2 の比率で余ったスペースが分配されていることがわかります。

flex-growを0:1:2にした際の図

flex-basis プロパティは、各アイテムの最初の取り分を設定するものという感じです。

今まで配分してきた「余ったスペース」は、コンテナの大きさから、各アイテムの本来の大きさを引いて求まるものでした。この「アイテムの本来の大きさ」を設定するのが flex-basis プロパティです。

例えば、flex-basis をそれぞれ 100px200px300px に設定した上で、flex-grow: 1; を設定してみましょう。

.card-free {
flex-basis: 100px;
flex-grow: 1;
}
.card-basic {
flex-basis: 200px;
flex-grow: 1;
}
.card-premium {
flex-basis: 300px;
flex-grow: 1;
}

初期のサイズは 100px、200px、300px であるものの、伸びていく比率は 1:1:1 であることが確認できます。

flex-basisを100px、200px、300pxにした際の図

なお、flex-basis のデフォルト値は auto となっており、この場合はアイテムの width が本来のサイズとして使用されます。 (flex-directioncolumn の場合は height です)

flex-shrink プロパティは、大まかには flex-grow の逆で、負のスペースを分配するものと考えればいいでしょう。つまり、コンテナのスペースがアイテムの合計の大きさに対して不足している場合に、各アイテムをどう縮小するかを指定するものです。

flex-shrink にそれぞれ 012 を指定してみましょう。

.card-free {
flex-shrink: 0;
}
.card-basic {
flex-shrink: 1;
}
.card-premium {
flex-shrink: 2;
}

Free のカードは縮まず、Basic と Premium のカードはそれぞれ 1:2 の比率で縮んでいっていることがわかります。

flex-shrinkを0:1:2にした際の図

なお、このプロパティのデフォルト値は 1 なので、何も設定しなくてもスペースが不足した場合には勝手にアイテムが縮みます。

注意すべき点として、実は flex-shrink の比率の計算方法は flex-grow とは異なります。flex-grow の場合は、単純に各アイテムの flex-grow の値の比で分配されました。しかし、flex-shrink の場合は、各アイテムの flex-shrink の値に flex-basis (で指定されたサイズ) を掛けたもの の比で分配されます。そのため、flex-basis で指定されたサイズが均等でない場合は、flex-shrink の値が同じであっても、縮み方が異なります。詳しくは、flex-shrink と flex-basis の組み合わせ - 主軸方向のフレックスアイテムの比率の制御 - CSS | MDN を参照してください。

flex-growflex-shrinkflex-basis の 3 つのプロパティは、flex プロパティを使って一括で指定することができます。

例えば、このように書くと、

.foo {
flex: 1 2 30%;
}

次のように各プロパティを設定したのと同じことになります。

.foo {
flex-grow: 1;
flex-shrink: 2;
flex-basis: 30%;
}

さらに短く書くこともでき、例えば

  • flex: 1;flex: 1 1 0%;
  • flex: auto;flex: 1 1 auto;
  • flex: none;flex: 0 0 auto;

の意味になります。詳細なルールは flex - CSS | MDN を参照してください。

flex-growflex-shrinkflex-basis についての詳細は、主軸方向のフレックスアイテムの比率の制御 - CSS | MDN を参照してください。