主軸方向のフレックスアイテムの比率の制御

このガイドでは、主軸に沿ってアイテムの寸法と自由度を制御することができる 3 つのプロパティ、 flex-grow, flex-shrink, flex-basis を見ていきます。これらのプロパティがどのようにフレックスアイテムを伸縮させるかについて完全に理解することが、 CSS フレックスボックスをマスターするための鍵です。

最初に見てみる

3 つのプロパティは、フレックスアイテムの自由度を以下の観点から制御します。

  • flex-grow: このアイテムが得る正の余白はどれくらいか。
  • flex-shrink: このアイテムから縮小できる負の余白はどれくらいか。
  • flex-basis: 伸長や縮小が発生する前のアイテムの寸法はいくつか。

プロパティは通常、一括指定の flex プロパティを使用して表します。以下のコードは flex-grow プロパティを 2 に、 flex-shrink1 に、 flex-basisauto に設定します。

css
.item {
  flex: 2 1 auto;
}

主軸に合わせて動作する重要概念

flex プロパティを理解するためには、伸縮が起こる前のフレックスアイテムの自然なサイズを知ることが役立ちます。さらに、主軸に沿ったすべてのフレックスアイテムの自然なサイズを組み合わせたものと主軸自体のサイズとの差である自由空間の概念を理解することも重要です。

フレックスアイテムの寸法の変更

フレックスアイテムをレイアウトするために利用できる空間を決定するために、ブラウザーはアイテムがどれくらいの大きさから始まるのかを知る必要があります。絶対的な長さの単位で適用される幅や高さを持たないアイテムは、どのようにして計算されるのでしょうか。

CSS では、 min-contentmax-content キーワードを <length> 単位の代わりに使用することができます。一般的に、 min-content は要素が最も長い単語を収めることができる最小のサイズであり、 max-content は要素が折り返されることなくすべてのコンテンツを収めるために必要なサイズです。

下記の例は、異なる文字列を持つ 2 つの段落要素を格納しています。最初の段落の幅は min-content です。テキストが利用利用可能なソフトラップの機会をすべて使用して、はみ出すことなく可能な限り小さくなっていることに注目してください。これがその文字列の min-content サイズです。基本的に、文字列の中で最も長い単語がサイズを決定しています。

2 つ目の段落は、 max-content の値を持ち、その逆を行います。ソフトラップの機会を取ることなく、コンテンツに合わせて必要なだけ大きくなります。コンテナー内で狭すぎるとはみ出します。

html
<p class="min-content">
  I am sized with min-content and so I will take all of the soft-wrapping
  opportunities.
</p>
<p class="max-content">
  I am sized with max-content and so I will take none of the soft-wrapping
  opportunities.
</p>
css
.min-content {
  width: min-content;
  border: 2px dotted rgb(96 139 168);
}
.max-content {
  width: max-content;
  border: 2px dotted rgb(96 139 168);
}

この動作と、min-content および max-content がどのような効果を持つかについては、後述の flex-growflex-shrink の説明で覚えておいてください。

正と負の余白

正と負の余白の概念も理解する必要があります。フレックスコンテナーに正の余白がある場合、コンテナーの中にはフレックスアイテムを表示するのに要求される以上の空間があります。例えば、横幅 500px のコンテナーで、 flex-directionrow に設定し、横幅 100px のフレックスアイテムを 3 つ格納した場合、 200px の正の余白があります。コンテナー内を埋め尽くしたい場合は、この正の余白をアイテムの間に分配することができます。

アイテムを配置した後で空間が残っていることを表す画像です。

フレックスコンテナーに負の余白があるというのは、フレックスコンテナー内で利用できる空間よりも、フレックスアイテムの自然なサイズの合計値の方が大きい場合です。上記の 500px 幅のコンテナー内で、 3 つのフレックスアイテムの幅が 100px ではなく 200px の場合、それらの自然な幅の合計は 600px となり、 100px の負の余白が生じます。この空間をアイテムから除去してコンテナー内に収まるようにしないと、アイテムがはみ出してしまいます。

アイテムがコンテナーからはみ出してしまう

フレックス一括指定のプロパティの成分を知るためには、この正の余白の分配と負の余白の除去を理解する必要があります。

以下の例では、flex-directionrow (行) に設定されているので、アイテムの寸法は幅に基づいて決定されます。すべてのアイテムの幅の合計とコンテナーの幅を比較して、正と負の余白を計算します。それぞれの例を flex-direction: column で試すこともできます。主軸が列になり、アイテムの高さと、アイテムが入っているコンテナーの高さを比較して、正と負の余白を計算する必要が出てきます。

flex-basis プロパティ

flex-basis プロパティは、正または負の余白を分配する前のフレックスアイテムの初期サイズを指定します。このプロパティの初期値は auto です。このプロパティは、 width および height プロパティと同じ値を受け入れ、 content キーワードも受け入れます。

flex-basisauto に設定されている場合、アイテムの初期サイズは、主要サイズが設定されていれば、その <length-percentage> サイズになります。例えば、あるアイテムに width: 200px が設定されている場合、 200px がそのアイテムの flex-basis になります。パーセント値は、フレックスコンテナーの内部の主要サイズに対する相対値です。 width: 50% が設定されている場合、このアイテムの flex-basis はコンテナーのコンテンツボックスの幅の半分になります。そのようなサイズが設定されていない場合、つまりアイテムが自動サイズである場合、 auto はそのコンテンツのサイズに解決され(上記の min- および max-content のサイズの説明を参照)、 flex-basis はアイテムの max-content という意味になります。

この例では、 flex-growflex-shrink の両方を 0 に設定した、柔軟性のないフレックスアイテムが 3 つ含まれています。 最初のアイテムの幅は 150px で、 flex-basis150px に設定されていますが、他の 2 つのアイテムは幅が設定されていないため、コンテンツの幅または max-content に応じてサイズが設定されます。

html
<div class="box">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
</div>
css
.box > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
  flex: 0 0 auto;
}

.box {
  width: 500px;
  border: 2px dotted rgb(96 139 168);
  display: flex;
}

.box :first-child {
  width: 150px;
}

auto キーワードとそれ以外の有効な width 値に加えて、content キーワードを flex-basis として使用することができます。この場合、flex-basisは、アイテムに width が設定されていてもコンテンツの寸法に基づきます。これは、設定されている幅を除去し、flex-basis として auto を使用するのと同じ効果を生み出します。 max-content を設定するのと似ていますが、 content の値を設定すると、交差軸上の寸法に基づいて aspect-ratio を計算することができます。

余白の分配の際にフレックスアイテムのサイズを完全に無視したい場合は、 flex-basis0 に設定し、 flex-grow0 以外の値を設定します。この値を実際に見ていく前に、 flex-grow を学びましょう。

flex-grow プロパティ

flex-grow プロパティは、フレックス伸長係数を指定します。これは、正の余白が分配されたときに、フレックスアイテムがフレックスコンテナー内の他のフレックスアイテムに対してどれだけ伸長するかを決定します。

すべてのアイテムの flex-grow 係数が同じであれば、正の余白はすべてのアイテムに均等に分配されます。この場合、一般的には flex-grow: 1 を設定しますが、任意の値、例えば 881001.2 などが設定できます。コンテナー内ですべてのフレックスアイテムに同じ係数が設定され、正の余白がある場合、その余白は均等に分配されます。

flex-growflex-basis の組み合わせ

flex-growflex-basis がどのように相互作用するかという点については混乱しやすいものです。コンテンツの長さが異なる 3 つのフレックスアイテムに、次のような flex ルールを適用した場合を考えてみましょう。

css
.class {
  flex: 1 1 auto;
}

この場合、 flex-basis の値は auto で、アイテムには幅が設定されていないので、寸法は自動調整されます。つまり、使用される flex-basis は各アイテムの max-content の寸法です。アイテムを配置した後、フレックスコンテナー内には正の余白があります。下記画像では斜線を引いた領域で示されています。斜線を引いた領域は正の余白で、 flex-grow 係数に基づいて 3 つのアイテムに分配されます。

3 つのアイテムが幅の半分強を占め、残りの幅は斜線が引かれています。

コンテンツの寸法に等しい flex-basis を使用しています。すなわち、利用できる空間全体(フレックスコンテナーの幅)から引き算で分配可能な余白が求められ、残りの余白は 3 つのアイテム間で均等に分配されます。最も大きなアイテムは、もともとより大きなサイズから始まり、他と同じ空間が分配されるので、最も大きなままになります。

ハッチングされた部分は3つに分割され、それぞれのアイテムに単一の部分が追加されました。

元の要素の大きさが異なっていても、3 つのアイテムを同じ大きさにしたいのであれば、 flex-basis 成分を 0 にしてください。

css
.class {
  flex: 1 1 0;
}

ここでは、空間分配の計算のために、各アイテムの寸法を 0 に設定しています。これによって、すべての空間が分配に利用できるようになります。すべてのアイテムが同じ flex-grow 係数をを持っていますので、それぞれ等しい大きさの空間を取得します。この結果、 3 つの同じ幅のフレックスアイテムができます。

このライブサンプルで、 flex-grow 係数を 1 から 0 に変更して、動作の違いを確認してみてください。

html
<div class="box">
  <div>One</div>
  <div>Two</div>
  <div>Three にはもっとコンテンツがあります</div>
</div>
css
.box > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
  flex: 1 1 0;
}

.box {
  width: 400px;
  border: 2px dotted rgb(96 139 168);
  display: flex;
}

アイテムに個別の flex-grow 係数を与える

flex-growflex-basis を一緒に使用すると、異なる flex-grow 係数を設定することで、個々のアイテムの大きさを制御することができます。すべての空間を分配できるように flex-basis0 にしておくと、それぞれのアイテムに異なる flex-grow 係数を割り当てることで、異なる大きさのフレックスアイテムを作成することができます。

下記の例では、最初の2つのアイテムには flex-grow の係数として 1 を使用し、3つ目には倍の 2 を使用しています。 flex-basis: 0 をすべてのアイテムに設定すると、利用できる空間は次のように配分されます。

  1. すべての兄弟フレックスアイテムの flex-grow 係数値が一斉に追加されます(この場合合計は 4 です)。
  2. フレックスコンテナー内で正の自由空間をこの合計値で割ります。
  3. 自由空間は個々の値に応じて分配されます。この場合、最初のアイテムは 1 部、 2 つ目は 1 部、 3 つ目は 2 部を取得します。ということは、 3 つ目のアイテムは最初と 2 つ目のアイテムの 2 倍の大きさということになります。
html
<div class="box">
  <div class="one">One</div>
  <div class="two">Two</div>
  <div class="three">Three</div>
</div>
css
.box > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
  flex: 1 1 0;
}

.box {
  border: 2px dotted rgb(96 139 168);
  display: flex;
}

.one {
  flex: 1 1 0;
}

.two {
  flex: 1 1 0;
}

.three {
  flex: 2 1 0;
}

ここでは、どんな正の値も使用できることを覚えておいてください。重要なのはアイテム間の比率です。大きな数字や小数を使用することができますが、それはあなた次第です。これを検証するために、この例の flex-grow の値を .25.25.50 に変えてみてください。同じ結果になるはずです。

flex-shrink プロパティ

flex-shrink プロパティでは、フレックス縮小係数を指定します。これは、負の余白が分配されたときに、フレックスコンテナー内の他のフレックスアイテムに対して、フレックスアイテムがどれだけ縮小するかを決定します。

このプロパティは、フレックスアイテムの flex-basis の結合値が大きすぎてフレックスコンテナー内で収まらず、あふれてしまう場合に対処します。アイテムの flex-shrink が正の値である限り、アイテムはコンテナー内であふれないように縮小されます。

flex-grow は伸長可能なアイテムに利用できる空間を追加するために使用しますが、 flex-shrink は、アイテムがあふれず、コンテナー内で確実に収まるように空間を縮小するために使用します。

次の例では、 3 つの 200px 幅のフレックスアイテムが 500px 幅のコンテナー内にあります。 flex-shrink0 に設定すると、アイテムは縮小されず、コンテナー内からあふれてしまいます。

html
<div class="box">
  <div>One</div>
  <div>Two</div>
  <div>Three にはもっとコンテンツがあります</div>
</div>
css
.box > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
  flex: 0 0 auto;
  width: 200px;
}

.box {
  width: 500px;
  border: 2px dotted rgb(96 139 168);
  display: flex;
}

flex-shrink 値を 1 に変更すると、各アイテムは同じ量だけ縮小され、すべてのアイテムがコンテナー内で収まります。それぞれのアイテムから負の余白が比例して除去され、それぞれのフレックスアイテムは最初の幅よりも小さくなります。

flex-shrinkflex-basis の組み合わせ

一見、 flex-shrinkflex-grow と同じように、要素を伸長させるのではなく、縮小させる方法で動作するように見えるかもしれません。しかし、注意すべき重要な違いがいくつかあります。

フレックスベースサイズの概念は、フレックスアイテム間で負の余白をどのように配分するかに影響します。負の余白を分配するときは、フレックス縮小係数にフレックスベースサイズを掛けます。これにより、アイテムがどれだけ縮むことができるかに比例して、負の余白が分配されます。例えば、大きなアイテムが顕著に縮小する前に、小さなアイテムがゼロに縮小することはありません。

小さなアイテムは min-content の寸法未満に縮小されることはありません。これは、使用できるソフトラップの機会をすべて使用した場合の要素の最小寸法です。

以下の例では、 min-content が下限になり、 flex-basis が内容物の大きさに解決されていることがわかります。フレックスコンテナーの幅を 700px などに変更してからフレックスアイテムの幅を小さくすると、最初の 2 つのアイテムは折り返されますが、 min-content の寸法より小さくなることはありません。ボックスが小さくなると、3 つ目のアイテムが縮小します。

html
<div class="box">
  <div>Item One</div>
  <div>Item Two</div>
  <div>Item Three はもっとコンテンツがあるため、もっと大きくなります</div>
</div>
css
.box > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
  flex: 1 1 auto;
}

.box {
  border: 2px dotted rgb(96 139 168);
  width: 500px;
  display: flex;
}

実際には、この縮小動作は妥当な結果をもたらします。コンテンツが完全に消えてしまったり、コンテンツの最小サイズより小さくなってしまったりするのを防ぎます。上記のルールは、コンテナーに合わせて縮小する必要があるコンテンツには理にかなっています。

アイテムに別の flex-shrink 要素を与える

flex-grow と同様に、それぞれのフレックスアイテムに異なる flex-shrink 係数を与えることができます。これにより、たとえば、あるアイテムを兄弟アイテムよりも急速に縮小させたり、まったく縮小させないようにしたい場合に、既定の動作を変更することができます。

この例では、最初のアイテムの flex-shrink 係数は 1、 2 つ目のアイテムは 0(まったく縮小しない)、 3 つ目のアイテムは 4 で、合計の縮小係数は 5 です。したがって、 3 つ目のアイテムは最初のアイテムの約 4 倍の速さで縮小しますが、どちらも min-content 幅以下に縮小することはありません。 flex-grow と同様に、ここでも小数や大きな数値を使用することができます。

html
<div class="box">
  <div class="one">One</div>
  <div class="two">Two</div>
  <div class="three">Three</div>
</div>
css
.box > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
  width: 200px;
}

.box {
  display: flex;
  width: 500px;
  border: 2px dotted rgb(96 139 168);
}

.one {
  flex: 1 1 auto;
}

.two {
  flex: 1 0 auto;
}

.three {
  flex: 2 4 auto;
}

フレックスアイテムの寸法の設定をマスターする

フレックスアイテムのサイズ調整がどのように動作するのかを理解するには、このガイドで説明した下記の要素を考慮する必要があります。

何がアイテムの基本的な寸法を決定するか

  • flex-basisauto に設定されていて、アイテムに幅が設定されている場合は、その幅に応じた寸法になります。
  • flex-basisauto に設定されていて、アイテムの幅が設定されていない場合は、コンテンツのサイズに基づいた寸法になります。
  • flex-basis が長さまたはパーセント値の単位で、0 ではない場合は、これがアイテムの寸法になります(min-content が下限になります)。
  • flex-basis0 に設定されている場合は、アイテムの寸法は空間共有の計算には考慮されません。

利用できる余白があるか

アイテムが伸長するのは、正の余白がある場合のみであり、負の余白がないとアイテムは縮小しません。

  1. すべてのアイテムの幅(列の場合は高さ)を合計したとき、その合計がコンテナーの幅(または高さ)の合計よりも小さい場合は、正の余白があり、flex-grow の出番となります。
  2. すべてのアイテムの幅(列の場合は高さ)を合計した場合、その合計がコンテナーの幅(または高さ)の合計よりも大きい場合は、負の余白があり、flex-shrink が効いてきます。

余白を分配する別の方法

アイテムに余白を追加したくない場合は、「フレックスコンテナー内のアイテムの配置」で説明している配置プロパティを使って、アイテム間やアイテム周辺の余白を管理できることを思い出してください。 justify-content プロパティを使用すると、アイテム間やアイテム周辺の余白の分配が可能になります。また、フレックスアイテムに auto のマージンを使用すると、スペースを吸収してアイテム間に隙間を作ることができます。

これらのフレックスプロパティがすべて利用できるため、最初のうちは少し試行錯誤が必要かもしれませんが、ほとんどのレイアウト課題が可能であることがわかります。