🌐

【CSS】 <table>タグをCSS gridで実現する

2023/04/18に公開

<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: tabledisplay: 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プロパティの書式は/区切りで始まりと終わりのグリッド位置を指定します。始まりについてはもとの位置から始まってほしいのでautospanの値は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を作成するときに--colCSS変数をつけることで列数に応じたグリッドが生成されます。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の機能は表組みだけでなく、いろんな場面で使えるテクニックなので覚えておくと良いでしょう。

脚注
  1. 筆者が見たところ、プロパティの値を変更せずtable-cell値のままでもレイアウトに影響はなさそうですが、バグ予防のためにdisplay: block変更しています。 ↩︎

Discussion