【CSS】 <table>タグをCSS gridで実現する
<table>
タグは表を表示するのに使うHTMLタグですが、表示のレイアウト挙動が独特です。例えばheight
プロパティがmin-height
っぽく振る舞ったり、table-layout
プロパティの値をfixed
にしないとwidth
が効かなかったりと慣れが必要です。ほかにもcolspan
属性やrowspan
属性によってセルの結合ができるので、レイアウトがHTMLとも密接した関係があります。
かつてはページレイアウトに必須だった時代もある歴史のあるタグですが、現代においては少し使いづらい点があります。一方でCSS Gridが登場し、柔軟なグリッドレイアウトが可能になっています。
そこで<table>
タグを利用しつつもCSS Gridでレイアウトを実現する方法を紹介します。
▼実際のサンプル
以下のHTMLをもとに解説していきます。
<table>
<thead>
<th>見出し</th>
<th>見出し</th>
<th>見出し</th>
<th>見出し</th>
<th>見出し</th>
</thead>
<tbody>
<tr>
<th>見出し</th>
<td>セル</td>
<td>セル</td>
<td>セル</td>
<td>セル</td>
</tr>
<tr>
<th>見出し</th>
<td colspan="2">セル横結合</td>
<td>縦<br />に<br />長<br />い<br />セル</td>
<td>セル</td>
</tr>
<tr>
<th>見出し</th>
<td>とっても横に長〜いセル</td>
<td rowspan="2">セル縦結合</td>
<td>セル</td>
<td>セル</td>
</tr>
<tr>
<th>見出し</th>
<td>セル</td>
<td>セル</td>
<td>セル</td>
</tr>
<tr>
<th>見出し</th>
<td>セル</td>
<td>セル</td>
<td>セル</td>
<td>セル</td>
</tr>
</tbody>
</table>
表は一番上段が縦の見出し列、一番左の列が横の見出し列となっています。セルの一部は横や縦に結合しています。そのほか横や縦に長いセルもあります。表としてありそうなシチュエーションで作成してました。これをCSS Gridでレイアウトしてみます。
デフォルトのプロパティを上書きする
<table>
タグや<td>
タグにはそれぞれdisplay: table
、display: table-cell
といったレイアウトに関するプロパティがかかっています。これらをCSS Gridにするために上書きします。
table {
display: grid;
thead,
tbody,
tfoot,
tr {
display: contents;
}
th,
td {
display: block;
}
}
まず、<table>
タグをCSS Gridに対応するためdisplay: grid
をあてます。つづいて、<table>
タグの子要素になる、<thead>
タグなどや<tr>
タグをレイアウト制御から外したいのでdisplay: contents
を付与します。そしてセルにあたる<th>
と<td>
タグをdisplay: block
に変更します[1]。こうすることで、<table>
タグに設定したCSS Gridに基づいて<th>
と<td>
タグが配置されるようになります。
列をつくる
今回作成するのは5列の表なのでgrid-template-columns
プロパティを使って列を規定します。
table {
display: grid;
+ grid-template-columns: repeat(5, auto);
}
grid-template-columns: repeat(5, auto)
とすることでauto
幅を5つ作るという指定になります。列幅については個別に指定できるよう、スタイル作成時はauto
としています。
セル結合に対応する
テーブルの特徴はセルの結合できる点です。CSS Gridでも同様に結合できるようにします。CSS gridにおける各グリッドへのレイアウトはgrid-column
およびgrid-row
プロパティを利用します。値の指定にspan
キーワードを用いることで複数グリッドをまたがるように指定ができます。
colspan="2"
のような2列にまたがるセルについて、このspan
キーワードを用いた指定をすることでcolspan
と同様のレイアウトができます。grid-column
プロパティの書式は/
区切りで始まりと終わりのグリッド位置を指定します。始まりについてはもとの位置から始まってほしいのでauto
をspan
の値はcolspan
属性の値と同じにします。
th,
td {
display: block;
+ &:[colspan="2"] {
+ grid-column: auto / span 2;
+ }
+ &:[rowspan="2"] {
+ grid-row: auto / span 2;
+ }
}
rowspan
属性についても同様の指定をします。このようなCSSでセルの結合ができます。ただし、結合数の動的な指定ができないので、結合するセルの数だけ予めスタイルとして用意しておかなければなりません。汎用的なコンポーネントとして利用する場合は、十分な数まで作っておくなどの対策が必要かもしれません。
見た目を整える
表組みの構造ができたので見た目を整えていきます。
基本
基本的なセルの大きさや背景を設定します。
table {
tbody {
th {
width: 100px;
}
}
th {
background-color: #ddd;
padding: 8px;
}
td {
padding: 8px;
background-color: #f9f9f9;
min-height: 40px;
}
}
一番左の見出し列は100px
で固定し、各コンテンツのセルは最小高さ40pxの可変幅です。見出しは濃いグレー、コンテンツは薄いグレー背景にしています。
隙間のある表組み
罫線を引かずに少し隙間を空けたデザインを見たことあるのではないでしょうか?gap
プロパティを使うと簡単に実現できます。
table {
gap: 2px;
}
レイアウトをCSS Gridで作っているので各セル間はgap
プロパティで調整できます。
罫線
オーソドックスな罫線で区切られた表組みのデザインです。border
プロパティで実現できますが、<table>
タグのようにborder-collapse
プロパティで線を重ねられないので、各セルの右と下にborder
を設定し、1番左側と上側は<table>
タグにborder
を設定して全体に罫線をかけています。
table {
border-top: 1px solid #333;
border-left: 1px solid #333;
th,
td {
border-bottom: 1px solid #333;
border-right: 1px solid #333;
}
}
見出しセル固定
モバイル環境では左側の見出しセルのみ固定し、内容のセルはスクロールしたい時もあります。 その時は見出しセルにposition: sticky
を使うことで比較的簡単にできます。
table {
width: 240px;
overflow: auto;
position: relative;
th:first-child {
position: sticky;
left: 0;
}
td {
min-width: 80px;
}
}
注意したいのは表の大きさがセルの合計サイズより小さいので、そのままでは限界までセルの幅が縮んでしまいます。そのためmin-widthで最小幅を設定しています。ただし、横に長いセルも一律で最小幅になってしまうので、大きくしたいセルがあれば、別途幅を指定する必要があります。
列数をある程度柔軟に対応する
CSSでgrid-template-columns: repeat(5, auto)
と指定しているように、列数をCSSで指定する必要があります。colspan
のときのように予め複数種類用意することもできますが、書き方を工夫することである程度は可変対応できます。
可変対応する場合は、<table>
タグのグリッドの列数設定をCSS変数を介するようにします。そして、<table>
タグにインラインスタイルで直接CSS変数を指定します。コードは次のようになります。
<table style="--col:5">
</table>
table {
grid-template-columns: repeat(var(--col), auto);
}
HTMLを作成するときに--col
CSS変数をつけることで列数に応じたグリッドが生成されます。HTMLの内容に応じた動的なものは難しいですが、少なくともスタイルは1つで済むので楽です。
セル結合にも対応する
同様にcolspan
属性などによるセル接合にも対応できます。
<td colspan="2" style="--colspan:2"></td>
<td rowspan="2" style="--rowspan:2"></td>
td {
// デフォルトのスパン
--colspan: 1;
--rowspan: 1;
grid-column: auto / span var(--colspan);
grid-row: auto / span var(--rowspan);
}
デフォルトのスパン1をCSSで定義しつつ、スパンをつなげたいときはインラインスタイルで指定する形です。これで予め作っておく必要はないですが、代わりに都度都度HTMLでインラインスタイルにて定義する必要があり一長一短です。表の規模や使用場面がある程度わかっているなら可変の方がむしろ面倒かもしれません。セル結合の可変対応は場面に応じて使いわけた方が良いと思います。
メリット・デメリット
<table>
タグのもつレイアウト機能ではなく、自前のグリッドで行うメリットはCSSによるカスタマイズがしやすい点です。さきほど説明した見出し固定の表であったり、モバイルで表組みを変更する場合などもCSSで制御でき、柔軟なレイアウトが可能です。
デメリットとしては可変対応が難しい点です。先述のとおり、ある程度対応可能ですが完全な意味での可変対応は難しいです。また、これは自分の調査不足ではあるのですが、アクセシビリティ上の懸念があります。HTMLの意味構造は変わりありませんが、レイアウトが変更になるので読み上げなどに影響があるかもしれません。その場合はrole
属性で補足するなどの追加が必要になります。(そうなると<table>
タグで組む必要性が薄れてきますが…)
まとめ
<table>
タグの持つレイアウト機能を上書きしてCSS Gridで実現する方法を紹介しました。ただ万能な方法ではなく、デメリットもあるので<table>
タグで自由なレイアウトを行いたいというときのみに使うのが無難かなという印象です。
しかしながら、ここで紹介したCSS Gridの機能は表組みだけでなく、いろんな場面で使えるテクニックなので覚えておくと良いでしょう。
-
筆者が見たところ、プロパティの値を変更せず
table-cell
値のままでもレイアウトに影響はなさそうですが、バグ予防のためにdisplay: block
変更しています。 ↩︎
Discussion