フレキシブルボックス レイアウト 完全解説
内容
基本概念
フレキシブルボックス レイアウトは、W3Cの仕様書「CSS Flexible Box Layout Module Level 1, W3C Candidate Recommendation, 1 March 2016」によって定義されている。従来、floatプロパティ等によって裏技的に行われてきた、段落ボックスを横並びにするといった配置を行うためのスタイルである。その名称どおり、単なる横並びだけでなく、より柔軟に配置を行う手段が用意されている。
仕様書では、以下のとおり、その概要が述べられている。
Flex layout is superficially similar to block layout. It lacks many of the more complex text- or document-centric properties that can be used in block layout, such as floats and columns. In return it gains simple and powerful tools for distributing space and aligning content in ways that web apps and complex web pages often need. The contents of a flex container:
- can be laid out in any flow direction (leftwards, rightwards, downwards, or even upwards!)
- can have their display order ‘reversed’ or rearranged at the style layer (i.e., visual order can be independent of source and speech order)
- can be laid out linearly along a single (main) axis or wrapped into multiple lines along a secondary (cross) axis
- can “flex” their sizes to respond to the available space
- can be aligned with respect to their container or each other on the secondary (cross)
- can be dynamically collapsed or uncollapsed along the main axis while preserving the container’s cross size
CSS Flexible Box Layout Module Level 1, W3C Candidate Recommendation, 1 March 2016
構造
フレキシブルボックス レイアウトの構造は、以下の図のごとく定義されている。
フレキシブルボックス レイアウトを実現する領域を表す要素をフレックス コンテナ、その領域内で実際に配置される要素をフレックス アイテムと呼ぶ。
フレックス アイテム(以下、アイテム)が配置される方向を軸(axis)と呼び、当該言語の文字と行の送りに応じて、文字送りが主軸(main axis)、行送りが交差軸(cross axis)と定義される。日本語横組みのテキスト、すなわち文字が左から右へ、行が上から下へ送られる場合、水平方向が主軸、垂直方向が交差軸となる。一方、日本語縦組みテキストでは主軸が垂直方向、交差軸が水平方向となる。
これらの軸の方向を表すものとして、始点と終点が定義される。日本語横組みテキストの場合、左端が主始点(main start)、右端が主終点(main end)、上端が交差始点(cross start)、下端が交差終点(cross end)である。startとendという概念は、関連プロパティの値に反映されている。
また、主軸方向の”幅”を主サイズ(main size)、交差軸方向の”高さ”を交差サイズ(cross size)と呼ぶ。すなわち、フレックス コンテナ(以下、コンテナ)の幅と高さということである。当然、縦組みテキストにおいては、主サイズが高さ、交差サイズが幅となるのは、自明の理であろう。
コンテナの定義
任意の要素をコンテナとして定義するには、displayプロパティを使用する。この値をflexもしくはinline-flexにすることで、当該要素はコンテナとなる。
- 例1 display: flex;
- 例2 display: inline-flex;
displayプロパティそのものは、CSS Level 2以前の太古の昔から存在するが、その値がCSS Flexible Box Layout Module Level 1モジュールにおいて拡張されたことになる。
コンテナとなる要素の態様に目を向けてみるならば、値がflexの場合、当該要素はブロックレベル表示で扱われる。故に、display: blockの場合と同じように、元来インライン表示のa要素に適用し、コンテナ要素自体に幅や高さを指定することが可能になる。
同様に、値がinline-flexの場合はdisplay: inline-blockと同等であり、幅や高さが指定できる段落内文字列としてコンテナ要素が扱われる。
そのうえで、コンテナ要素の直接の子要素に対し、フレキシブルボックスによる配置が適用されるようになる。なお、コンテナの要素内容がテキストだった場合でも、その文字列全体をひとつのアイテムとして、配置が適用される。
ただし、アイテムが要素ではなく文字列のみの場合は、アイテム側専用のプロパティを、要素内容(文字列)に対して使用することはできない。
コンテンツの送り
flex-direction
日本語横組みテキストの場合、アイテムは、デフォルトでは主軸の始点から終点に向かって送られる。この送りを定義するプロパティが、flex-directionであり、デフォルト値はrowで表される。すなわち、行方向の送りである。このプロパティは、コンテナ側に指定する。
- 例 flex-direction: row;
そして、row-reverse値では、配置を逆転させることが可能である。
- 例 flex-direction: row-reverse;
flex-directionプロパティ 値一覧 | |
---|---|
適用対象 | フレックス コンテナ |
row(デフォルト) | main startからmain endへ送る(横組み時に左から右へ送る) |
row-reverse | main endからmain startへ送る(横組み時に右から左へ送る) |
column | cross startからcross endへ送る(縦組み時に右から左へ送る) |
column-reverse | cross endからcross startへ送る(縦組み時に左から右へ送る) |
なお、縦組み時においては、column値もしくはcolumn-reverse値で送り方向を制御する。すなわち、交差軸方向の送りである。
order
flex-directionは、アイテム全体の順序を一括して規定することになるわけだが、個々のアイテムの配置順を定義する、orderプロパティも用意されている。値は整数値で、昇順で指定する。
- 例 order:1;
数の少ない方が、多いものよりも上位であり、flex-directionの送り方向に従って先頭寄りに配置される。
デフォルト値は0で、この場合の順序は、異なる任意の数値を指定したアイテムが存在しない限り、すなわち全てがデフォルトの0であるなら、アイテムのHTMLソースコードにおける記述順となる。
0と1では、0の方が上位となるため、例えば3つのアイテム中の1つに1を指定すると、そのアイテムの順位は最下位となる。
また、同じ数値を与えたアイテムの順序は、それらの間での記述順に規定される。すなわち、orderプロパティの値は、順位グループなのである。
依って、0以上の異なる数値を指定したアイテム同士において、orderプロパティによる個別の順序が実現されることになる。
なお、0のアイテムよりも上位に配置する場合は、マイナスの数値を指定する。
故に、orderを指定しない(デフォルト値の0である)アイテム――HTMLコードの記述順に並んだアイテム――の中で、:hoverや:targetなどを使用して1つのアイテムだけに-1を与えれば、当該アイテムを最上位に移動させることが可能である。
当然だが、このプロパティはアイテム側に指定する。
コンテンツの幅
アイテムがコンテナ内に配置された際、アイテム サイズの総計が主サイズを超える場合は、デフォルトで主サイズに収まるように縮小される。アイテムにmarginが適用されている場合、それを含むアイテムのサイズが、縮小の対象となる。結果として、アイテムに指定されているwidthやmarginの値は無視される。
flex-basis
アイテムの実サイズ(マージンを含む)を無視した上で、各アイテムの幅はflex-basisの設定によって定義される。これは、アイテム側に指定する。デフォルト値はautoであり、各アイテムの要素内容の比率もしくはアイテムの(width等による)既存サイズの比率に応じて縮小される。
また、flex-basisプロパティでは、%や任意の単位(px等)で値を指定することもできる。この場合、アイテムに指定されているwidthプロパティ等のサイズよりもflex-basisプロパティの値が優先され、サイズが決定される。
- 例 flex-basis:250px;
もちろん、flex-basisプロパティによるサイズ指定が主サイズを超過した場合、もしくは既存サイズとflex-basisサイズの混合によっても超過した場合、やはりアイテムは主サイズに合わせて縮小される。
flex-shrink
この縮小率のみを専門に定義するプロパティとしてflex-shrinkがあり、こちらは比率を整数値で相対的に指定する。デフォルト値は1である。ただし、その計算はやや煩雑である。
例えば、ここまでの例のように、主サイズ1000pxのコンテナに、widthで200px, 300px, 400px, 400pxに幅を指定した4つのアイテムが入り、主サイズを300px超過するものとしよう。flex-basisはautoとし、widthでアイテムのサイズが規定されている。その上で、flex-shrinkプロパティ値をすべて1とする。
この結果として、アイテムは2:3:4:4
の比率で縮小される。
すなわち、flex-shrinkの1という値は、アイテム同士を1:1:1:1
の比率に強制するものではなく、アイテム毎の既存サイズに対する比率であり、それは「既存サイズ÷flex-shrinkプロパティ値」となる。故に、縮小される比率は、2÷1:3÷1:4÷1:4÷1=2:3:4:4
で変化しない。
そこで、flex-shrinkプロパティ値の関係を2:1:1:2
とすると、2÷2:3÷1:4÷1:4÷2=1:3:4:2
の比率で、アイテムは縮小される。
このように、既存サイズを前提にした縮小比率の調整が、flex-shrinkプロパティの役割である。値がアイテム同士の相対的比率を表すものではないことに、注意が必要であろう。
なお、flex-shrinkプロパティの値を0にすると、アイテムの縮小が無効化される。この場合、コンテナの要素のoverflowプロパティがvisible(デフォルト)であれば、アイテムはコンテナから突き出し、hiddenであればコンテナ内でトリミングされる。
flex-grow
アイテム サイズの総計が主サイズ以下の場合は、コンテナは変形されず、そのまま配置される。その場合、flex-growプロパティで比率を整数値で相対指定すれば、主サイズに合わせてアイテムを伸長させることができる。デフォルト値は0で、この場合コンテナは伸長されない。
flex-growプロパティ値も、flex-shrinkと同様に、アイテム毎の既存サイズに対する比率であり、こちらは「既存サイズ×flex-growプロパティ値」となる。例えば、1000pxの主サイズの中に100pxと200pxの既存サイズを持つアイテムが、それぞれ2つ配置されている場合、アイテム同士の比率は1:1:2:2
である。これらすべてにflex-grow:1を指定すると、1x1:1x1:2x1:2x1=1:1:2:2
となり、既定サイズを基にした比率で伸長される。
これに対し、flex-growプロパティ値を4:3:2:1
で配分するならば、伸長は1x4:1x3:2x2:2x1=4:3:4:2
の比率で、“理論上”は行われることになる。
しかし、その結果は、比率が完全に一致するとは限らない。下図は、Microsoft Edgeでの結果だが、各アイテムは260px, 220px, 280px, 240pxの幅となり、比率は13:11:14:12
となった。IE11、Firefox、Chromeも、まったく同じ結果である。
また、アイテムの幅を100pxに揃えたうえで、4:3:2:1
の比率で伸長を指定しても、結果は、340px, 280px, 220px, 160px(17:14:11:8
)となり、正確な比率で伸長されるものではない。
よって、flex-growプロパティによって正確なサイズ指定をすることは、不可能であろう。
flex
なお、flex-grow, flex-shrink, flex-basisの省略形として、flexプロパティがある。
flexプロパティ 値構成 | |
---|---|
適用対象 | フレックス アイテム |
第一値 | flex-growの値 |
第二値 | flex-shrinkの値 |
第三値 | flex-basisの値 |
故に、これらのデフォルト値は、以下のように表される。
そして、この指定は以下と同義である。
backgroundのような省略形と異なり、flexは接頭辞が「flex-」のプロパティ全てを統括するものではなく、あくまでも上記3つの省略形に過ぎないことに留意されたい。
flexプロパティは何の役に立つのか?
ここまで、flex(flex-grow, flex-shrink, flex-basis)プロパティを見てきたわけだが、これらは一体何の役に立つのだろうか? フレキシブルボックス レイアウトを、例えば従来のfloatプロパティを利用したボックスの段組に置き換えるものとするなら、こうしたボックスの配置では、主サイズ、すなわちコンテナの領域に収まるように、初めからアイテムのサイズを調整しているはずである。それらがきちんとデザインされていれば、サイズ指定はflex-basisに頼らなくともwidthで十分であり、伸縮も不要である。だが、それだけでflexを退けてしまうのは、早計に失するであろう。
確かにサイズが固定されている場合には、flexプロパティはあまり役立たないかもしれないが、サイズを成り行きに任せるレイアウトでは、どうだろうか。
例えば、ul要素をコンテナとし、li要素をアイテムとして配置するリンクリストがあるとしよう。当初、li要素数は4個、そして先頭と末尾のアイテムは、他の2倍の幅とする。
これをサイズ固定で考えると、主サイズが1000pxであれば、約166.6pxと約333.3pxが、それぞれのアイテムの幅になる。まずここで、ピクセル値に小数点が付くことになってしまう。なぜかというに、基準となるサイズは1000pxに対し1/6、2倍の方は2/6、すなわち1/3となるからである。当然、1÷6や1÷3は整数にはならないので、小数点が付く。これらをパーセントに置き換えても、同様である。
とはいえ、小数点以下の値の発生だけが、問題なのではない。これ自体は、いわゆるCSS3のcalc関数を値に使えば解決できることである。すなわち、1000pxという絶対値であろうと、100%という相対値であろうと、以下の如く記述すれば済む。
以上が絶対値の場合である。相対値では、以下のとおりである。
小数点以下数値の発生を、calc関数で回避したとしても、次の問題を考慮せねばならない。アイテムの数が増えた場合にはどう対処するのか、これである。仮にアイテム数が7個に増えるとするなら、相対値の場合、calc(100% / 9)およびcalc((100% / 9) *2)に書き換えなければならない。
では、アイテム(li要素)が4個の場合で、以下のflex指定を行ったとしよう。
ここでは、アイテムに明示的な幅は指定せず、flex-growで1と2を指定しているのみである。2の指定には:first-childおよび:last-child擬似クラスを使用して、対象を先頭と末尾のアイテムに特定している。
結果として、上図の如く、両端とそれ以外のアイテムで2:1の幅が実現された。
では、ここにアイテムのli要素を3個増やし、7個にしてみる。結果は、以下のとおりである。
結果は、やはり両端のアイテムは、他のアイテムに対し、相対的に2倍の幅を維持できている。
この変更は、HTMLソースコードにli要素を追加したのみで、CSSは一切変更していない。すなわち、同一のCSSで、HTML側の変更に対処し得たわけである。
また、コンテナ(ul要素)の幅を80%、4個のアイテムのうち、3個を幅100pxの固定サイズとするが、最後の1つは成り行きにしたいとする。指定は以下のとおりである。
ここでは、:not疑似クラスで4つ目(last-child)のli要素を除外し、flex:0 1 100pxで伸長なし、縮小比率1、幅100pxに固定している。一方、4つ目のli要素のみ、flex:1 0 autoで伸長比率1、縮小なし、幅自動で、残りの領域すべてを占有するようにした。
この場合、他に伸長させるものはないので、flex-grow値の1は、伸長をオンにするスイッチとも言える。
flex-growプロパティ値は、比較対象のアイテムが無ければ、実際のところ1でも100でも何でも構わない。
パーセンテージで幅を指定したコンテナは、画面(もしくは親要素)に応じて伸縮するが、その中で3つのli要素(上図item1~item3)は固定幅を維持し、最後のli要素(item4)だけを、残余空間を埋めるように伸長させることができる。すなわち、固定幅と可変幅の共存である。
そして、アイテムが7つに増えた場合でも、スタイルシートを変更することなく、これらの動作は維持される。
さらには、コンテナが縮小し、固定サイズのアイテムが入りきらなくなった場合でも、これらのflex-shrink値が1になっているので、同じ比率で縮小され、コンテナに必ず収納される。
flex-growは、サイズ合わせにこそ使えなかったが、固定幅のアイテムと成り行きのアイテムを共存させるケースでは、このように役立つのである。なおかつ、flex-shrinkも組み合わせることで、メディアクエリーによるサイズ変更にも、柔軟に対応できるだろう。まさに、フレキシブルボックスの、言葉通りの“柔軟性”を活かせるのである。
コンテンツの配置
justify-content
ここまで見てきた、アイテムを主軸方向に配置させる領域がフレックス行(flex line)なのだが、アイテムサイズの総計が主サイズに満たない場合、この行内で主軸方向の揃えをjustify-contentプロパティで定義できる。
アイテム サイズ総計が主サイズを超えた場合はアイテム間の余白が実質的に表示されないため、揃えを指定しても知覚はできない。
- 例 justify-content: flex-end;
ここでは、左や右といった概念ではなく、startもしくはendで配置の関係を表す。このプロパティは、コンテナ側に指定する。
justify-contentプロパティ 値一覧 | |
---|---|
適用対象 | フレックス コンテナ |
flex-start(デフォルト値) | main startに寄せる。いわゆる左揃え |
flex-end | main endに寄せる。いわゆる右揃え |
center | 中央揃え |
space-between | 余白をアイテムの間に均等に分配する |
space-around | 余白をアイテム間に均等に、main start、main endにはその半分の幅で分配する |
space-evenly | 余白をmain start、main end、アイテム間に均等に分配する(IE非対応) |
以下の図は、テキストが横組み時の配置である。
align-items
フレックス行内では、アイテムの交差軸方向のサイズ(実質的な高さ)は、デフォルトで行の高さ(交差サイズ)に伸長され、すべてのアイテムの交差軸方向サイズが揃う。これを制御するプロパティがalign-itemsで、交差軸方向サイズを揃えるデフォルト値はstretchである。
- 例 align-items: flex-start;
このプロパティは、コンテナ側に指定する。
align-itemsプロパティ 値一覧 | |
---|---|
適用対象 | フレックス コンテナ |
stretch(デフォルト値) | 交差サイズに伸長 |
flex-start | cross startに寄せる。いわゆる上揃え |
flex-end | cross endに寄せる。いわゆる下揃え |
center | 交差サイズの中央に寄せる。いわゆる縦中央揃え |
baseline | アイテム内テキストのベースラインに揃える。 |
以下の図は、テキストが横組み時の配置である。
アイテムの高さは、アイテム側にheight等の指定がなければ要素内容によって決まる。故に、コンテナ側にも高さの指定がない場合、交差サイズは最も大きなアイテムの高さに準ずることとなる。そして、stretchの場合では、すべてのアイテムが最大サイズにまで引き伸ばされる。
なお、同様の設定をアイテム側に個別に指定する、align-selfプロパティもある。
align-selfプロパティ 値一覧 | |
---|---|
適用対象 | フレックス アイテム |
auto(デフォルト値) | コンテナ側のalign-items設定に合わせる |
stretch | 交差サイズに伸長 |
flex-start | cross startに寄せる。いわゆる上揃え |
flex-end | cross endに寄せる。いわゆる下揃え |
center | 交差サイズの中央に寄せる。いわゆる縦中央揃え |
baseline | アイテム内テキストのベースラインに揃える。 |
マルチライン
flex-wrap
フレキシブルボックス レイアウトでは、デフォルトでは1行(シングルライン)のフレックス行にアイテムが収められるが、主サイズを超えるアイテムを折り返し、複数行(マルチライン)で表示させることも可能である。これを制御するのがflex-wrapプロパティで、コンテナ側に指定する。
- 例 flex-wrap: wrap;
flex-wrapプロパティ 値一覧 | |
---|---|
適用対象 | フレックス コンテナ |
nowrap(デフォルト値) | 折り返し無し(シングルライン) |
wrap | main endで折り返し、マルチラインにする |
wrap-reverse | main endで折り返し、cross endからcross start方向(下から上)へ送るマルチラインにする |
flex-flow
なお、アイテムの送り方向を指定するflex-directionと、flex-wrapを組み合わせた、省略形(ショートハンド)のプロパティflex-flowも用意されている。
- 例 flex-flow: row wrap;
flex-flowプロパティ 値構成 | |
---|---|
適用対象 | フレックス コンテナ |
第一値 | flex-directionの値 |
第二値 | flex-wrapの値 |
折り返しは、アイテムの合計サイズが主サイズを超えた場合にのみ行われる。それが主サイズに達しない状態では、強制的に折り返す手段は無い。
故に、任意のアイテムを末端にして折り返すには、当該行内アイテムの幅をwidthやflex-basis等のプロパティで調節することになる。
仮に、主サイズに達しない状態で折り返すとすれば、アイテムにmarginを適用するしかない。
align-content
マルチラインのフレキシブルボックス レイアウトでは、各行同士で交差軸方向の揃えを指定することも可能である。すなわち、justify-contentをマルチラインの配置に適用するようなもので、align-contentプロパティを使用する。
- 例 align-content: flex-start;
このプロパティは、コンテナ側に指定する。
align-contentプロパティ 値一覧 | |
---|---|
適用対象 | フレックス コンテナ |
stretch(デフォルト値) | 全ての行を伸長して交差サイズに合わせる |
flex-start | 全ての行をcross startに寄せる。いわゆる上揃え |
flex-end | 全ての行をcross endに寄せる。いわゆる下揃え |
center | 全ての行を交差サイズの中央に寄せる |
space-between | 余白を行間に均等に分配する |
space-around | 余白をcross start、cross end、行間に均等に分配する |
以下の図は、テキストが横組み時の配置である。
各行の高さは、シングルラインの場合と同様に、含まれるアイテムの最大サイズによって、行毎に決定される。
orderプロパティによるアイテムの順序も、マルチラインにおいて貫徹されるが、各行によるアイテムの分割に関わりなく、全ての行を通して適用される。例えば、他はすべてデフォルトのままで、最終行にある1つのアイテムだけにorder:-1を指定すれば、当該アイテムは第1行の先頭へ移動する。
逆に言えば、行毎に分けて順序を指定する手段は、存在しない。
フレキシブルボックスの応用
フレキシブルボックス レイアウトは、基本的には従来のfloatプロパティによるボックス配置の代替手段と言える。しかし、それ以外にも、align-itemsプロパティを利用すれば、CSS2には存在しなかったボックス内の垂直方向配置が可能になる。
たとえば、リンクボタンとしてのa要素を作成するため、高さを固定サイズとし、要素内容の文字列を垂直方向中央揃えにするといったケースである。従来は、ブロックレベル表示化したa要素内に、やはりブロックレベル表示にしたspan要素を組み込み、そこにpadding-topを挿入して文字列を押し下げることで、擬似的な中央揃えを実現した。
このうち、span要素を組み込む構造は、a要素に直接padding-topを適用すると高さの調節が必要になることを回避することが目的だった。これに関しては、いわゆるCSS3のbox-sizing: border-boxで、paddingがサイズに含まれないようにすることが可能になったので、span要素を省くことは可能である。
box-sizingはIE8も対応している。
しかし、高さが固定サイズの場合、内容の文字サイズを勘案して、padding-top値を決めねばならないことに変わりはない。となれば、高さや文字サイズが変更されたなら、その都度padding-top値を変更する必要がある。
では、このa要素をフレキシブルボックスのコンテナにすると、どうだろうか。先に述べたとおり、display: flexを指定することで、ブロックレベル化も適用される。
この時点で、a要素がコンテナ、要素内容の文字列はアイテムとなる。文字列をspan要素等で囲む必要は、無い。文字列のみでは、アイテム側のスタイル指定ができないが、ここではコンテナ側の指定のみで目的を達成できる。
まず水平方向の揃えだが、アイテムを主軸方向で揃えるjustify-contentプロパティでcenterを指定すれば、文字列がその字数を幅として、中央揃えになる。
コンテナとしての文字列にjustify-content値が適用できるのであれば、align-items値も同様であろうことは、言うまでもなかろう。
以上のように、display: flex, justify-content: center, align-items: centerの3つをa要素に指定するだけで、水平/垂直方向の中央揃えが可能となるのである。