table-layoutの勉強
<table>
要素苦手過ぎる。克服したい
Tailwind UIのTable(有料なのでソースは貼れない)のマークアップ、すごいことになっていて、多分それぞれのプロパティに理由があるんだけど分かんなすぎる。
preflightがかかった状態での最小限のtableスタイリングについて考えたい。
最小限のテーブルデータをここから持ってきた。
何もスタイルがない状態だとこんな感じ。
この時点でかかっているCSSリセットの内容(テーブル関連のみ)。
table {
text-indent: 0;
border-color: inherit;
border-collapse: collapse;
}
*, ::before, ::after {
box-sizing: border-box;
border: 0 solid #e5e7eb;
}
table要素はdisplay: tableがあたっているが、これは特殊な挙動を示す模様。
この記事は素晴らしい。
- 絶対にカラム落ちしない
- すべてのカラムの高さは、一番縦長と同じ高さになる
これを防ぐためにはoverflow: hidden;を設定すれば回避できると想像される方もいるかもしれません。しかしながら、table-cellにはそれが通用せず、強制的に収まるサイズまで伸びてしまうのです。
伸びてしまう問題を回避するためには、table全体の幅を明示するとともに、table-layout: fixed;を指定します。これでコンテンツ内容により、ほかのカラムの圧迫を阻止し、意図しない崩れが発生する可能性を回避できます。
横幅とtable-layout: fixed;を明示しておくことで、はじめてoverflow: hidden;を発動させることができます。テキストの折り返しを強制的に行いたい場合には、word-wrap: break-word;を合わせて指定しておきます。
これだ!!!
関係ないがfreshでtableを使って検証してるときにtableの包含関係で間違ったエラーが出る現象にあたった。
preactのバージョン更新で修正OK。近々latestでも修正されるはず
実際にはみ出し挙動を確認してみる。
このようにスペースが入らない単語では折り返しようがないため、はみ出してしまう。
(同様に横幅の広い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
の指定も必要だろう。
ついでにtable-fixedの挙動について確認しておく。
table-autoの場合はtable-cellが広がり放題になってしまう。
table-fixedを指定すると、テーブルの一行目にセットした幅が有効になり、残りはよしなに分配される。
titleにwidth="600"を設定するとこんな感じ。
例えばこのように設定すると、この全ての幅を足した 800px までは圧縮に耐える。
<tr>
<th width="100">Author</th>
<th width="600">Title</th>
<th width="100">Year</th>
</tr>
これ以上圧縮するとページ全体に横スクロールが発生してしまうので、tableのコンテナを作成しoverflow-x-auto
を設定してtable要素だけに横スクロールが出るようにすべきだろう。
table-fixed
を設定したときに、カラム固定幅は一部だけでもOKか、全体に設定すべきか?
基本的には確保したい最小幅として全カラムに指定すべきだと思う。
break-wordsを指定しない状態だと、下記のような状態になっても横スクロールが始まらない。
break-wordsを指定すると、最小幅が0になるのか、やはりこのような状態になっても横スクロールが発生しない。
widthを最低限欲しい幅として設定し、その合計値を横スクロールが始まるブレークポイントとして考えると良いのだと思う。
しかし、こうなるとtable-layout: auto
ってかなり使いづらくないか…?
URLが含まれるなど折り返しができないような状況になると、コンテンツ(全体のどこかのセル)のサイズを最優先してカラム幅が増えてしまう。タブ幅を明示的に指定できない(そうだっけ?試しても効かない)。全体をoverflow-x-autoでスクロールさせることはできる。レンダリングが遅くなる。
URLみたいな折り返せないものが入らなければいいのか・・・?
例えば行数や列数が少なく、w-full
しないような小さな表であれば、table-layout: auto
のほうがいいかもしれない。table-fixed
だと普通に2行目以降ではみだしが発生してしまう。
さて、ついでに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>
);
}
このあと上司に見てもらいながら修正したのだけど、tdの中身にdivを置いてmax-widthを設定するという方法を提案してもらって、それは思いつかなかったな…と思った。
ページネーションでコンテンツが変わるようなテーブルがガタつかないようにfixedを使うことはあるけど、多くの用途でautoで問題ないらしい(widthはtdの方に設定する)