flex box layout(フレックスボックスレイアウト)は要素の並びをコントロールする事のできるレイアウトモードです。
flexと似たような動きをする事ができるレイアウトモードにgrid layoutがありますが、使用する場面がそれぞれ事なり明確に適切な使用場面があります。
flex boxは一次元のレイアウトに対して使用し、gridは二次元のレイアウトに使用する事が適しています。
一次元、二次元だとイメージが付きにくいかもしれませんので以下のようにイメージしてください。
- 一次元は1つの軸に沿ってレイアウトを行うもので、列もしくは行どちらか1つの軸を使用するもの。
- 二次元は2つの軸に沿ってレイアウトを行うもので、列と行の2の軸を使用するもの。
軸(方向)はinlineとblockと同じように水平方向と垂直方向どちらかの方向にアイテムを並べる事ができます。
ですので、例えばメニューバーの各メニューアイテムは横一列に並べたい(1つの方向)なのでflexを使用するのが適しています。
一方、ページ全体、例えばheader,main,saidebar,footerや、複数行にわたるアイテムの並びはgridが適しています。
flexの使用
flex boxを使用するには並びを変更したいアイテムの親要素にdisplay: flex;を宣言します。
ポイントは実際にレイアウトが変更される要素と宣言を記載する要素が異なる点です。
display: flex;を宣言した要素自体のレイアウトモードはデフォルトのフローレイアウトのまま、その子供の要素がflexレイアウトに変化し、要素の並び方が変化します。
See the Pen Untitled by tora (@-tora-) on CodePen.
通常のフローレイアウトではblock要素は垂直方向に縦積みされますが、親にflexを宣言したことで横並びに変化しました。
flexでは並びの方向をinline方向(水平方向)もしくはblock方向(垂直方向)どちらかを選択できますが、デフォルトでは水平方向です。
次にその方向の指定方法について解説します。
flex-direction
flex boxはアイテムが並ぶ向きを水平方向と垂直方向の2つの軸で動作するを述べましたが、directionを使用する事でその軸の向きを変更する事が可能です。
軸の仕組み
flex boxにはアイテムが並ぶmain axis(主軸)と、main axisに垂直に交じわるcross axis(交差軸)があります。
flex-directionこのmain axisを行方向に配置するか、列方向に配置するかをコントロールする事が可能です。
ですのでflex-directionの値はrow(行)もしくはcolumn(列)の2種類です。
flex-directionのデフォルトの値はrowになります。
rowの場合、行方向、つまり、main axisは水平方向に配置され、cross axisは垂直方向に配置されます。
columnの場合、列方向、つまり、main axisは垂直方向に配置され、cross axisは水平方向に配置されます。
そしてrowとcolumnに対してそれぞれreverseをつける事ができます。
- row-reverse
- column-reverse
reverseをつけると軸の向きが反転します。
アイテムの並び方
flex box内のアイテムはmain axisに沿って並びますので、flex-directionの値によって以下のようにアイテムの並びをコントロールする事ができます。
justify-content
justify-contentを使用する事でmain axis上のアイテムの配置パターンを変更する事が可能です。
セット可能な値は以下になります。
- flex-start
- flex-end
- center
- space-between
- space-around
- space-evenly
startとend
フローレイアウトでmargin-block-startとあったように、flexにもstart,endがあります。
justify-contentはmain axis上の並びをコントロールしますので、felx-start,flex-endの動きは以下のようになります。
center
centerを使用する事でアイテムを中央に寄せる事が可能です。
space系
上2つはアイテムをくっつく形並んでいましたが、space-○○は空白を空けて並びます。
【space-between】
space-betweenはstartとend両端をくっつけたうえで、余った空間に均等に要素を配置します。
【space-around】
space-aroundは親要素を子要素の数で分割し、その中で中央に配置されます。
以下をご覧ください。
例えば親要素が240pxで、子要素が3つだった場合、以下のようになります。
横幅が240pxなので3で割って80pxです。
その要素をしきつめ、その要素の真ん中になるように子要素を配置するのがspace-aroundです。
【space-evenly】
space-evenlyは子要素を均等に配置します。
aroundは均等に割った枠の中で中央に配置しました。
言い換えれはアイテムの左右に同じ余白を持たせて横並びにするイメージです。
すると1つ目の右の余白と2つ目の左の余白が被ってしまいます。
それを解決し、全ての余白を均等にとるものがevenlyです。
align-items
align-itemsを使用する事でcross axis上のアイテムの配置パターンを変更する事が可能です。
セット可能な値は以下になります。
- stretch
- flex-start
- flex-end
- center
- baseline
stretch
stretchを使用すると子要素は親要素に対していっぱいに広がります。
startとend
justify-contentと同じようにstartの場合は開始位置、endの場合は終了の位置にアイテムを配置しますが、今回はalign-itemsなのでcross axis上です。
startはデフォルト位置で、毎度見ているので図解はendにします。
center
中央に配置されます。
baseline
baselineは少しイメージが難しいですが、ボックスモデルを思い出してください。
ボックスモデルのcontent部分の下の位置で要素を揃えます。
例えば、以下のように、contentに当たる数字の部分の下の位置がそろっています。
各3つのアイテムは数字の周りに大きさの異なるpaddingを使用しているので要素の多きさは変わりますがcontentの位置で中央揃えをしているようなレイアウトが可能です。
See the Pen Untitled by tora (@-tora-) on CodePen.
align-content
align-itemsは一行に並ぶアイテムの位置を決めるものでしたが、align-contentは複数行になった場合のアイテムの位置を決めることが可能です。
flexはデフォルトの状態ではアイテムは複数行にはなりません。
複数行にする方法は後程解説します。
align-contentもいままでやった位置決めプロパティと同様に考えれば理解しやすいです。
justify-contentのcross axisバージョンと言えます。
- stretch
- flex-start
- flex-end
- center
- space-between
- space-around
実際はあまり使用する事はありません。
align-itemsで事足ります。
align-self
いままでは親から子を一括で操作する事を行いました。
基本的にflex boxは1つの方向に一括でアイテムを並べる目的で使用されるのでほとんどの場合はいままでのプロパティの組み合わせでデザインを再現する事が可能です。
ですが、例えばある子要素のみ特別な並びに変更したいとなった場合どうしますか。
そんな時に活躍するのがalign-selfです。
align-selfは親要素ではなく、対象の子要素に指定します。
赤色のselfとなっているboxにalign-selfを適応した場合のイメージです。
align-selfはcross axisに対して作用します。
この機能を使用する事で以下のようなLineの画面を再現する事が可能です。
codepenのページに飛べば答えのコードを見る事は可能ですが、その前に一度自分で考えて、これと同じものをflex boxとalign-selfを使用して再現してみてください。
See the Pen Untitled by tora (@-tora-) on CodePen.
要素の縮小と拡大
flexで要素を横並びにしている場合、画面を縮めるとからなず子要素が親要素の幅よりも大きくなる時が来ます。
その際の要素はどのような動きをするでしょうか。
flex boxはデフォルトでは改行は起こりませんので、子要素は親要素に合わせて縮んでしまいます。
デフォルトの状態では同じ割合で各アイテムが縮んでいますが、flex-shrinkを使用する事で縮む比率を指定する事ができます。
flex-shrink: 0;
flex-shrinkに0を設定するとアイテムは縮みません。
これはよく、アイコンとテキストが横並びになている場面で使用されます。
See the Pen Untitled by tora (@-tora-) on CodePen.
2以上のflex-shrink
flex-shrinkに2以上の値をセットすると割合的にアイテムの縮み方が変化します。
例えば以下のような割合おアイテムが4つある場合。
- アイテム1 = 1
- アイテム2 = 2
- アイテム3 = 1
- アイテム4 = 1
各数字は全体に対しての割合を表しますので、まずすべての数字を足します。
1 + 2 + 1 + 1 = 5
という事は比率は100 / 5 = 20%です。
なので各アイテムの縮む割合はこうです。
- アイテム1 = 20%
- アイテム2 = 40%
- アイテム3 = 20%
- アイテム4 = 20%
他のアイテムが20px縮む間にアイテム2は40px縮みます。2倍の速さです。
赤色のアイテムがflex-shrink: 2のアイテムです。
flex-grow
flex-shrinkとは反対に伸びる割合を設定する事が可能です。
割合の考え方はshrinkと同じです。
- アイテム1 = 1 = 20%
- アイテム2 = 2 = 40%
- アイテム3 = 1 = 20%
- アイテム4 = 1 = 20%
shrinkを設定していないので縮む割合は一緒ですが、growを設定しているので画面幅を広げると赤のアイテム2が他のアイテムよりも2倍のスピードで大きくなります。
widthとflex-basis
flexにはflex-basisというプロパティが存在しますが、これは子要素の横幅を設定する事ができます。
横幅の設定と言えばwidthですが、flex-basisはwidthとは異なる動きをします。
widthとflex-basisのそれぞれの違いを見てましょう。
まずwidthとflex-basisが2つ設定されていた場合ですが、flex-basisが勝ちます。
flex-basisは縮む事のできる最小の状態までしか関与しません。
min-contentプロパティを思い出して下さい。
縮む事のできる最小の値はinline要素1つ1つの大きさになります。
日本語は1文字1文字がinlineのブロックですので、最小値は1文字の幅ですが、英語は1つのワードになり、ワードの途中で改行される事はデフォルトの状態では起こりません。
ですので今回の場合、「item」というワードの横幅が最小値です。
「item」というワード横幅はおよそ40pxです。
flex-basisは最小値である40px以下には関与しませんので、flex-basisを10pxにしても40px以下なので影響はありません。
flex-basisが影響しない値となったのでここでwidthを有効にします。
するとwidthが有効になり、要素の幅が強制的に10pxになり、中のcontentははみ出てしまいました。
あまり使用しませんが、widthとflex-basisにはこのような関係がありますので、もし横幅で不具合が出たときのために頭の片隅に置いておきましょう。
複数行のflexアイテム
要素の縮小と拡大では親要素の横幅が小さくなった時に各アイテムをどのように縮めるかという点について記載しました。
ですが、アイテムを小さくすると文字が読みづらくなるという点があります。
基本的にflex boxは1次元(一行)上にアイテムを配置する事に使用されるものですが、flex-wrapを使用する事でアイテムが親要素からoverflowする際に改行し、複数行で扱う事が可能です。
そうする事でアイテムが小さくなりすぎてみにくくなる問題を解決できます。
flex-wrap: wrap; // 改行します flex-wrap: nowrap; // 改行しません。デフォルト値
実装の際の注意点として、改行させるためにはアイテムに横幅を指定する必要があります。
min-contentやwidthを指定してコンテンツがこれ以上小さくならないようにする設定が必要です。
この設定が無いと子要素は縮む事が可能となってしまいますので永遠に改行されなくなってしまいます。
gap
いままでのflex boxの例では横並びに下アイテムの間に余白はなく、ぎゅうぎゅうに敷き詰められていました。
例なのでよいですが、一般的なデザインではそうはいきません。
基本的には余白をコントロールする必要が出てきますが、gapを使用すると各アイテムの余白を調整する事が可能です。
gapはわりと最近登場したもので、safariにいたってはほんの1年ほど前(2021年)にやっと対応しました。
まずはgapが存在する前はどのようにアイテムの余白を制御していたのかを見てみましょう。
See the Pen Untitled by tora (@-tora-) on CodePen.
gapを使用せずに余白を空ける方法はいくつかありますが、これが最も簡単な方法かと思います。
すべてのアイテムにmargin-leftを与えます。
余白を空ける事ができましたが、これでは初めのブロックの左にも余白が付いてしまいますのでflexをかけている親要素の左の余白をマイナスマージンで戻します。
すると相殺され、左の位置をずらす事なく、各アイテムに余白を与える事ができました。
ですがこれだと改行した時に問題が発生します。
上方向の余白を設定していないため、改行したときに上方向にアイテムがくっついてしまっています。
ですので同じようにmargin-topをつけ、親のmargin-topにマイナスマージンをつける事で相殺し、対応します。
これでも実装は可能ですが、マージンの相殺などすこし面倒ですよね。
その問題を簡単に解決できるものがgapです。
例えば左右を30px、上下を10pxの余白に設定したい場合は以下のように記載します。
gap: 30px 10px; // gap: row方向 column方向;
See the Pen Untitled by tora (@-tora-) on CodePen.
値の異なる余白
gapで簡単に余白を空ける事ができましたが、gapは各アイテムの間に均等な値をセットするものです。
例えば以下のように一部の余白は大きく、一部の余白は小さい値を設定したい場合はどうすればよいのでしょうか。
html
<header> <div class="logo">LOGO</div> <button class="download">資料ダウンロード</button> <button class="contact">お問い合わせ</button> </header>
1つの案としてはbuttonをdivで括ってアイテムを3つから2つにします。
そしてjustify-content: space-betweenを宣言すれば2つのアイテムは左右にぴったしくっつきます。
2つのbuttonの親であるdivにはflexを宣言し、gapを使用してbuttonの間の余白を作ります。
<style> header { display: flex; justify-content: space-between; } .button-area { display: flex; gap: 10px; } </style> <header> <div class="logo">LOGO</div> <div class="button-area"> <button class="download">資料ダウンロード</button> <button class="contact">お問い合わせ</button> </div> </header>
これでもよいですが、無駄にdivが増えてしまいました。
実はこの実装はhtml構造を変更する事なく対応可能です。
対応方法はいくつかありますが、こちらがシンプルで簡単に機能します。
<style> header { display: flex; gap: 10px; } .logo { margin-right: auto; } </style> <header> <div class="logo">LOGO</div> <button class="download">資料ダウンロード</button> <button class="contact">お問い合わせ</button> </header>
margin-rightにautoを使用して余白を全て使用するように上書きします。
もしくはflex-growを利用して余った分の余白を全て使うという方法でもいいかもしれません。
See the Pen Untitled by tora (@-tora-) on CodePen.
並び順の変更
flex boxはデフォルトではhtmlの並び順で要素が並びます。
例えば以下のようにitemが1,2,3,4と上から並ぶと要素は左から右に1,2,3,4と並びます。
<div> <div>item-1</div> <div>item-2</div> <div>item-3</div> <div>item-4</div> </div>
htmlでの要素の並びはそのままに見た目上で並びを変更させたいときはorderを使用します。
.item-1 { order: 2; } .item-2 { order: 1; }
要素を逆方向に配置したい時
orderを使用して要素の並びを変更する事はできますが、要素全てを逆方向から配置したい場合、全ての要素にorderを書く必要があるのでしょうか?
それは面倒ですね。
要素が増えればその分orderを追記しなければいけません。
そんな時はflex-directionです。
flex-directionはmain axisとcross axisを入れ替えるだけでなく、軸の向きを変更する事も可能です。
軸の向きを変更するとstartとendの向きも右から左、下から上に変わります。
flexが子に与える影響
flexによりフローレイアウトは解除され子要素のレイアウトモードが変化します。
レイアウトモードが変化した子要素にz-indexを与えてみるとどうなるでしょうか。
See the Pen Untitled by tora (@-tora-) on CodePen.
html上では要素1が初めに記載されており、その次に2の要素となります。
通常は要素1の上に要素2が重なるはずですが、要素2の上に要素1があります。
これはz-indexで重なり順をコントロールしているからなのですが、1つ疑問な事がポジションレイアウトを使用していないのにz-indexが有効な事です。
これはフレックスレイアウトモードにポジショニング機能が含まれているためz-indexが有効になります。
— 補足 —
レイアウトモードはいわば関数のようなもので、それ以外のプロパティは引数です。
同じ引数プロパティを与えても、実行する関数が異なればレンダリング結果も異なります。
フローレイアウトには重なりをコントロールする機能は実装されていないのでz-indexの引数を与えても影響はありませんが、positionプロパティを宣言し、ポジショニングレイアウトにモード(関数)を変更し、z-indexの引数を与える事で重なり順をコントロールできたのです。
— /補足 —
flex内でのmargin
flexを使用するとフローレイアウトからモードが変更されると記載しました。
レイアウトモードは2つ適応させる事はできません。
marginの特長としてmarginが重なった場合、吸収が起こりますが、これはフローレイアウトの場合です。
flexを使用してフローレイアウトが解除された今はmarginの吸収は起こりません。
See the Pen Untitled by tora (@-tora-) on CodePen.
デベロップツールで見るとmarginは薄いオレンジの部分になります。
marginを10pxとしていますが、吸収される事なく、合わせて20pxの余白が空いています。
flex内の要素はblockのような動きとなる
flex内ではmarginの吸収が起こりませんが、marginについてもう一つ考えるべきポイントがあります。
それはinline要素に対しても同じ動きをするのか。
という点です。
その答えは「同じ動きをする」となります。
ですが、少しおかしい話です。確かinlineにはblock方向のmarginをつける事ができません。
ということはflex内の要素はblock要素のような動きをするという事が考えられます。
その証拠に以下のデモを見てください。
See the Pen Untitled by tora (@-tora-) on CodePen.
2つの文章はどちらもspan要素です。
spanはinline要素ですが、inline要素を改行した時のblockとの違いを思いだしてください。
inlineは要素ごと改行し、blockは中のcontentが改行します。
その違いにより、例えばborder-bottomを追加すると、inlineの場合、borderも改行され複数行となり、blockはborderは改行されず1行のままです。
inline要素であるspanに全く同じスタイルをあて、flexの外とflexの中にそれぞれ配置すると、flexの中のようそがblockになった事がわかります。