Open13

table-layoutの勉強

hashrockhashrock

Tailwind UIのTable(有料なのでソースは貼れない)のマークアップ、すごいことになっていて、多分それぞれのプロパティに理由があるんだけど分かんなすぎる。

preflightがかかった状態での最小限のtableスタイリングについて考えたい。

hashrockhashrock

この時点でかかっているCSSリセットの内容(テーブル関連のみ)。

table {
    text-indent: 0;
    border-color: inherit;
    border-collapse: collapse;
}
*, ::before, ::after {
    box-sizing: border-box;
    border: 0 solid #e5e7eb;
}
hashrockhashrock

https://developer.mozilla.org/ja/docs/Web/HTML/Element/table

table要素はdisplay: tableがあたっているが、これは特殊な挙動を示す模様。

https://www.codegrid.net/articles/css-table-1/

この記事は素晴らしい。

  • 絶対にカラム落ちしない
  • すべてのカラムの高さは、一番縦長と同じ高さになる

これを防ぐためにはoverflow: hidden;を設定すれば回避できると想像される方もいるかもしれません。しかしながら、table-cellにはそれが通用せず、強制的に収まるサイズまで伸びてしまうのです。

伸びてしまう問題を回避するためには、table全体の幅を明示するとともに、table-layout: fixed;を指定します。これでコンテンツ内容により、ほかのカラムの圧迫を阻止し、意図しない崩れが発生する可能性を回避できます。

横幅とtable-layout: fixed;を明示しておくことで、はじめてoverflow: hidden;を発動させることができます。テキストの折り返しを強制的に行いたい場合には、word-wrap: break-word;を合わせて指定しておきます。

これだ!!!

hashrockhashrock

実際にはみ出し挙動を確認してみる。

このようにスペースが入らない単語では折り返しようがないため、はみ出してしまう。
(同様に横幅の広いimgなどでも起こり得る)

      <table class="table-fixed w-full">
        <tr>
          <th>Author</th>
          <th>Title</th>
          <th>Year</th>
        </tr>
        <tr>
          <td>Miguel De Cervantes</td>
          <td class="break-words">
            hogehogehogehogehogehohogehogehogehogehogehohogehogehogehogehogehohogehogehogehogehogehohogehogehogehogehogehohogehosssso
          </td>
          <td>1605</td>
        </tr>
...
</table>

このように、table-fixedの指定、w-fullによる横幅の明示、セルに対するbreak-wordsの指定でようやく折り返された(これらの指定が抜けると無限にセルが広がってしまう)。画像が入ることを考慮するとoverflow-x-hiddenの指定も必要だろう。

hashrockhashrock

ついでにtable-fixedの挙動について確認しておく。

table-autoの場合はtable-cellが広がり放題になってしまう。

table-fixedを指定すると、テーブルの一行目にセットした幅が有効になり、残りはよしなに分配される。

titleにwidth="600"を設定するとこんな感じ。

hashrockhashrock

例えばこのように設定すると、この全ての幅を足した 800px までは圧縮に耐える。

        <tr>
          <th width="100">Author</th>
          <th width="600">Title</th>
          <th width="100">Year</th>
        </tr>

これ以上圧縮するとページ全体に横スクロールが発生してしまうので、tableのコンテナを作成しoverflow-x-autoを設定してtable要素だけに横スクロールが出るようにすべきだろう。

hashrockhashrock

table-fixedを設定したときに、カラム固定幅は一部だけでもOKか、全体に設定すべきか?

基本的には確保したい最小幅として全カラムに指定すべきだと思う。

break-wordsを指定しない状態だと、下記のような状態になっても横スクロールが始まらない。

break-wordsを指定すると、最小幅が0になるのか、やはりこのような状態になっても横スクロールが発生しない。

widthを最低限欲しい幅として設定し、その合計値を横スクロールが始まるブレークポイントとして考えると良いのだと思う。

hashrockhashrock

しかし、こうなるとtable-layout: autoってかなり使いづらくないか…?

URLが含まれるなど折り返しができないような状況になると、コンテンツ(全体のどこかのセル)のサイズを最優先してカラム幅が増えてしまう。タブ幅を明示的に指定できない(そうだっけ?試しても効かない)。全体をoverflow-x-autoでスクロールさせることはできる。レンダリングが遅くなる。

URLみたいな折り返せないものが入らなければいいのか・・・?

例えば行数や列数が少なく、w-fullしないような小さな表であれば、table-layout: autoのほうがいいかもしれない。table-fixedだと普通に2行目以降ではみだしが発生してしまう。

hashrockhashrock

さて、ついでにtailwindでのテーブルの最低限のスタイリングも模索してみよう。

基本的にはこんな感じで十分だと思う。

import { JSX } from "preact";

export function Table(props: JSX.HTMLAttributes<HTMLDivElement>) {
  return (
    <div {...props} class="bg-white overflow-x-auto px-4 py-1 rounded-lg">
      <table class="w-full  divide-y divide-gray-400">
        <thead>
          <tr>
            <th class="py-4 text-left">
              Author
            </th>
            <th class="py-4 text-left" width="600">Title</th>
            <th class="py-4 text-left" width="100">Year</th>
          </tr>
        </thead>

        <tbody class="divide-y divide-gray-300">
          <tr>
            <td class="py-4">Mary Shelley</td>
            <td class="py-4">Frankenstein; or, The Modern Prometheus</td>
            <td class="py-4">1818</td>
          </tr>
          <tr>
            <td class="py-4">Herman Melville</td>
            <td class="py-4">Moby-Dick; or, The Whale</td>
            <td class="py-4">1851</td>
          </tr>
          <tr>
            <td class="py-4">Emma Dorothy Eliza Nevitte Southworth</td>
            <td class="py-4">The Hidden Hand</td>
            <td class="py-4">1888</td>
          </tr>
          <tr>
            <td class="py-4">F. Scott Fitzgerald</td>
            <td class="py-4">The Great Gatsby</td>
            <td class="py-4">1925</td>
          </tr>
          <tr>
            <td class="py-4">George Orwell</td>
            <td class="py-4">Nineteen Eighty-Four</td>
            <td class="py-4">1948</td>
          </tr>
        </tbody>
      </table>
    </div>
  );
}

hashrockhashrock

このあと上司に見てもらいながら修正したのだけど、tdの中身にdivを置いてmax-widthを設定するという方法を提案してもらって、それは思いつかなかったな…と思った。
ページネーションでコンテンツが変わるようなテーブルがガタつかないようにfixedを使うことはあるけど、多くの用途でautoで問題ないらしい(widthはtdの方に設定する)