一覧WB-09-2 · CSSレイアウト

FlexboxとGrid

三つの箱でFlexboxの伸長・縮小・軸・間隔を測った後、Gridの列配分と複数列にまたがる配置を確認します。最後に長い文字列を入れ、列の最小幅と内容の折返しを別々に調整します。寸法はCSS pxで、小数2桁へ丸めて表示します。

説明

Flexboxは主軸に沿った並びと伸縮、Gridは行と列の格子を使って要素を配置します。どちらも空いている幅を配分できますが、基準となるサイズや最小幅の条件によって、同じ指定から予想と違う幅が出ることがあります。

三つの箱でFlexboxの伸長・縮小・軸・間隔を測った後、Gridの列配分と複数列にまたがる配置を確認します。最後に長い文字列を入れ、列の最小幅と内容の折返しを別々に調整します。寸法はCSS pxで、小数2桁へ丸めて表示します。

1余った幅を均等に伸ばす

幅620px、gap=10pxのFlexboxに、基準幅100px・grow=1の箱を三つ置きます。

(globalThis.doc=createFormDocument('<!doctype html><html lang="ja"><head><style>body{font:16px/24px system-ui;margin:16px;background:#111;color:#eee}#flex{display:flex;width:620px;gap:10px箱の間の固定間隔;background:#222}.item{flex:1 1 100pxgrow・shrink・basisの順;min-width:0;height:40px;box-sizing:border-box;background:#22434b}.item:nth-child(2){background:#57304a}</style></head><body><div id="flex"><div id="a" class="item">A</div><div id="b" class="item">B</div><div id="c" class="item">C</div></div></body></html>'),globalThis.flex=doc.getElementById("flex"),globalThis.items=[...flex.children],items.map各箱の測定(e=>rect("#"+e.id)))

基準幅の合計300pxとgap二つの20pxを引くと、余りは300pxです。growがすべて1なので100pxずつ増え、各幅は約200pxになります。gapも配分前の必要幅に含めます。

2growの比率を変更

Bだけflex-growを2へ変えます。

(items[1].style.flexGrow="2"Bの伸長係数を2にする,items.map(e=>rect("#"+e.id).width))

余り300pxを1:2:1で分け、基準100pxに75・150・75pxを足します。幅は約175・250・175pxです。growの比は最終幅そのものの比ではなく、余った空間をどう分けるかの比です。

3基準幅に比例して縮める

親幅を320pxへ縮め、基準幅を100・200・100px、shrinkをすべて1にします。

(flex.style.width="320px"合計幅より狭い親,items.forEach((e,i)=>e.style.flex="0 1 "+(i===1?200:100)+"px"伸ばさず基準幅に応じて縮める),items.map(e=>rect("#"+e.id).width))

基準幅400pxとgap20pxに対して100px足りません。縮小量はshrink×基準幅の比1:2:1に従い、25・50・25pxです。最終幅は約75・150・75pxになります。縮小時はgrowと同じ式で余りを配るのではありません。

4縮小を止めるとあふれる

三つのshrinkを0にします。

(items.forEach(e=>e.style.flexShrink="0"縮小を禁止),{widths:items.map(e=>rect("#"+e.id).width),client:flex.clientWidth,scroll:flex.scrollWidth表示幅と内容幅})

箱は基準幅100・200・100pxを保ち、親幅320pxからはみ出します。scrollWidthは内容を含む必要幅、clientWidthは内側の表示幅です。Flexboxを指定すれば、どんな内容でも自動的に収まるわけではありません。

5justify-contentが配る余白

幅500px、各箱100pxで伸縮を止め、space-betweenを指定します。

(flex.style.width="500px",items.forEach(e=>e.style.flex="0 0 100px"),flex.style.justifyContent="space-between"余りを箱の間へ配分,{positions:位置から実際の間隔を確認items.map(e=>rect("#"+e.id).x),gap:styleOf("#flex","gap")})

箱の間は約100pxになります。gapの10pxに、残った180pxを二つの間へ90pxずつ加えるためです。CSSのgap値10pxだけが、見える箱の間隔の全てとは限りません。

6交差軸の位置をそろえる

親の高さを120px、align-itemsをcenterにします。

(flex.style.height="120px"縦方向に空間を用意,flex.style.alignItems="center"交差軸の中央へ,{parent:rect("#flex"),children:items.map(e=>rect("#"+e.id))})

高さ40pxの箱は、交差軸である縦方向の中央へ移ります。上下の空きは計算上それぞれ40pxです。rowの場合、justify-contentは横の主軸、align-itemsは縦の交差軸に作用します。

7columnでは主軸が縦になる

縦並びに変え、主軸の長さを200px、各箱の主軸サイズを30pxにします。

(flex.style.flexDirection="column"主軸を縦へ,flex.style.width="300px",flex.style.height="200px",flex.style.alignItems="flex-start",items.forEach(e=>{e.style.flex="0 0 30px"縦方向の基準サイズ;e.style.width="100px";e.style.height="30px"}),items.map(e=>rect("#"+e.id)))

justify-content:space-betweenが今度は縦方向へ作用します。30pxの箱三つの間へ残りを配り、位置の差は約85pxです。flex-directionを変えたときは、主軸と交差軸も読み替えます。

8Gridで固定列と比率列を作る

ここからは幅400pxのGridを使い、100px・1fr・2frの三列へ四つのセルを置きます。

(doc.querySelector("style").textContent+="#grid{display:grid;width:400px;grid-template-columns:100px 1fr 2fr固定幅と残余幅の比率;gap:10px;background:#222}#grid>div{height:40px;background:#22434b}",doc.body.innerHTML='<div id="grid"><div id="g1">1</div><div id="g2">2</div><div id="g3">3</div><div id="g4">4</div></div>',globalThis.grid=doc.getElementById("grid"),{columns:styleOf計算された列幅("#grid","grid-template-columns"),cells:各セルの行と列の位置[...grid.children].map(e=>rect("#"+e.id))})

固定100pxとgap二つ20pxを引いた280pxを、1:2で分けます。残りの二列は約93.33pxと186.67pxです。四つ目のセルは次の行へ配置されます。

9等しい二列へ変更

列を二つのminmax(0, 1fr)へ変えます。

(grid.style.gridTemplateColumns="repeat(2,minmax(0,1fr))最小幅0の比率列を二つ",{columns:styleOf("#grid","grid-template-columns"),cells:[...grid.children].map(e=>rect("#"+e.id))})

gap10pxを除いた390pxを二分し、各列は約195pxです。四セルは二行二列になります。minmax(0,1fr)は列の最小幅を0とする指定で、内容による最小幅との関係は後で確認します。

10一つのセルを二列にまたがらせる

最後のセルをspan 2にします。

(doc.getElementById("g4").style.gridColumn="span 2"二列分の領域を使う,rect("#g4"))

最後のセルは新しい行で二列にまたがり、幅は約400pxです。195pxの列二つだけでなく、その間のgap10pxも含む領域になります。

111frの最小幅に内容が影響する

長い一語を入れたセルと短いセルを、幅240pxの二列へ配置します。

(grid.style.width="240px",grid.style.gridTemplateColumns="1fr 1fr"自動の最小幅を持つ比率列,grid.style.fontFamily="monospace",grid.innerHTML='<div id="long"></div><div id="short">B</div>',doc.getElementById("long").textContent="W".repeat(40)途中に空白のない長い文字列,{columns:styleOf("#grid","grid-template-columns"),client:grid.clientWidth,scroll:grid.scrollWidthあふれた内容幅})

長い一語が必要とする最小幅の影響で、二列が親幅の中へ収まりません。1frは、どんな内容でも親の半分まで縮める指定ではなく、自動の最小幅が関わります。

12列の幅と文字のあふれは別

列の最小幅を0へ変更します。

(grid.style.gridTemplateColumns="repeat(2,minmax(0,1fr)列の最小幅を0にする)",{columns:styleOf("#grid","grid-template-columns"),long:rect("#long")箱の幅,client:grid.clientWidth,scroll:grid.scrollWidth文字のあふれは残る})

列はそれぞれ約115pxになりますが、長い文字自体はまだ折り返されず、内容が外へはみ出します。列を縮めることと、文字を折り返すことは別です。

13長い語の折返しを許す

長い文字列にoverflow-wrap:anywhereを指定し、高さも内容に合わせます。

(doc.getElementById("long").style.overflowWrap="anywhere"長い語の途中でも改行を許可,doc.getElementById("long").style.height="auto"内容に合わせて高さを確保,{long:rect("#long"),client:grid.clientWidth,scroll:grid.scrollWidth})

文字が列幅の中で折り返され、横方向のあふれが解消します。トラックの配分と内容の改行・最小幅を合わせて設計すると、長い入力に対しても配置を保ちやすくなります。

まとめ

Flexboxのgrowは余りの配分、shrinkは基準幅も考慮した縮小です。軸の向きも確認します。

Gridのfr・gap・spanは、列幅とセルの領域を決めます。

列や箱の幅を縮める処理と、内容の折返し・あふれの扱いは分けて調整します。

操作履歴と解説を振り返れます。

ブラウザ / HTML・DOM

1
(globalThis.doc=createFormDocument('<!doctype html><html lang="ja"><head><style>body{font:16px/24px system-ui;margin:16px;background:#111;color:#eee}#flex{display:flex;width:620px;gap:10px;background:#222}.item{flex:1 1 100px;min-width:0;height:40px;box-sizing:border-box;background:#22434b}.item:nth-child(2){background:#57304a}</style></head><body><div id="flex"><div id="a" class="item">A</div><div id="b" class="item">B</div><div id="c" class="item">C</div></div></body></html>'),globalThis.flex=doc.getElementById("flex"),globalThis.items=[...flex.children],items.map(e=>rect("#"+e.id)))
[
  {
    "x": 16,
    "y": 16,
    "width": 200,
    "height": 40
  },
  {
    "x": 225.99,
    "y": 16,
    "width": 200,
    "height": 40
  },
  {
    "x": 435.99,
    "y": 16,
    "width": 200,
    "height": 40
  }
]
2
(items[1].style.flexGrow="2",items.map(e=>rect("#"+e.id).width))
[
  174.99,
  250,
  175
]
3
(flex.style.width="320px",items.forEach((e,i)=>e.style.flex="0 1 "+(i===1?200:100)+"px"),items.map(e=>rect("#"+e.id).width))
[
  75,
  150,
  75
]
4
(items.forEach(e=>e.style.flexShrink="0"),{widths:items.map(e=>rect("#"+e.id).width),client:flex.clientWidth,scroll:flex.scrollWidth})
{
  "widths": [
    100,
    200,
    100
  ],
  "client": 320,
  "scroll": 420
}
5
(flex.style.width="500px",items.forEach(e=>e.style.flex="0 0 100px"),flex.style.justifyContent="space-between",{positions:items.map(e=>rect("#"+e.id).x),gap:styleOf("#flex","gap")})
{
  "positions": [
    16,
    216,
    416
  ],
  "gap": "10px"
}
6
(flex.style.height="120px",flex.style.alignItems="center",{parent:rect("#flex"),children:items.map(e=>rect("#"+e.id))})
{
  "parent": {
    "x": 16,
    "y": 16,
    "width": 500,
    "height": 119.99
  },
  "children": [
    {
      "x": 16,
      "y": 55.99,
      "width": 100,
      "height": 40
    },
    {
      "x": 216,
      "y": 55.99,
      "width": 100,
      "height": 40
    },
    {
      "x": 416,
      "y": 55.99,
      "width": 100,
      "height": 40
    }
  ]
}
7
(flex.style.flexDirection="column",flex.style.width="300px",flex.style.height="200px",flex.style.alignItems="flex-start",items.forEach(e=>{e.style.flex="0 0 30px";e.style.width="100px";e.style.height="30px"}),items.map(e=>rect("#"+e.id)))
[
  {
    "x": 16,
    "y": 16,
    "width": 100,
    "height": 30
  },
  {
    "x": 16,
    "y": 101,
    "width": 100,
    "height": 30
  },
  {
    "x": 16,
    "y": 186,
    "width": 100,
    "height": 30
  }
]
8
(doc.querySelector("style").textContent+="#grid{display:grid;width:400px;grid-template-columns:100px 1fr 2fr;gap:10px;background:#222}#grid>div{height:40px;background:#22434b}",doc.body.innerHTML='<div id="grid"><div id="g1">1</div><div id="g2">2</div><div id="g3">3</div><div id="g4">4</div></div>',globalThis.grid=doc.getElementById("grid"),{columns:styleOf("#grid","grid-template-columns"),cells:[...grid.children].map(e=>rect("#"+e.id))})
{
  "columns": "100px 93.33px 186.67px",
  "cells": [
    {
      "x": 16,
      "y": 16,
      "width": 100,
      "height": 40
    },
    {
      "x": 125.99,
      "y": 16,
      "width": 93.33,
      "height": 40
    },
    {
      "x": 229.32,
      "y": 16,
      "width": 186.67,
      "height": 40
    },
    {
      "x": 16,
      "y": 65.99,
      "width": 100,
      "height": 40
    }
  ]
}
9
(grid.style.gridTemplateColumns="repeat(2,minmax(0,1fr))",{columns:styleOf("#grid","grid-template-columns"),cells:[...grid.children].map(e=>rect("#"+e.id))})
{
  "columns": "195px 195px",
  "cells": [
    {
      "x": 16,
      "y": 16,
      "width": 195,
      "height": 40
    },
    {
      "x": 220.99,
      "y": 16,
      "width": 195,
      "height": 40
    },
    {
      "x": 16,
      "y": 65.99,
      "width": 195,
      "height": 40
    },
    {
      "x": 220.99,
      "y": 65.99,
      "width": 195,
      "height": 40
    }
  ]
}
10
(doc.getElementById("g4").style.gridColumn="span 2",rect("#g4"))
{
  "x": 16,
  "y": 115.99,
  "width": 400,
  "height": 40
}
11
(grid.style.width="240px",grid.style.gridTemplateColumns="1fr 1fr",grid.style.fontFamily="monospace",grid.innerHTML='<div id="long"></div><div id="short">B</div>',doc.getElementById("long").textContent="W".repeat(40),{columns:styleOf("#grid","grid-template-columns"),client:grid.clientWidth,scroll:grid.scrollWidth})
{
  "columns": "319.96px 8px",
  "client": 240,
  "scroll": 338
}
12
(grid.style.gridTemplateColumns="repeat(2,minmax(0,1fr))",{columns:styleOf("#grid","grid-template-columns"),long:rect("#long"),client:grid.clientWidth,scroll:grid.scrollWidth})
{
  "columns": "114.99px 115px",
  "long": {
    "x": 16,
    "y": 16,
    "width": 114.99,
    "height": 40
  },
  "client": 240,
  "scroll": 320
}
13
(doc.getElementById("long").style.overflowWrap="anywhere",doc.getElementById("long").style.height="auto",{long:rect("#long"),client:grid.clientWidth,scroll:grid.scrollWidth})
{
  "long": {
    "x": 16,
    "y": 16,
    "width": 114.99,
    "height": 72
  },
  "client": 240,
  "scroll": 240
}

演習完了