表(テーブル)をCSSで見やすく|縞模様・スマホで横スクロール・見出しと左の列の固定、コピペ4種類と使いどころ

表をCSSで見やすく。縞模様・横スクロール・見出しと左の列の固定の見本とコード

料金表、営業時間、送料の一覧。表(テーブル)はたくさんの情報を一度に見せられる便利な部品ですが、そのままだと「行がどこまで続いているか追いにくい」「スマホで右が切れる」「下へ進むと何の列か分からなくなる」といったことが起きます。どれも、CSSを数行足すだけで直せます。

使いどころは「行や列が多くて、比べながら読む表」です。3〜4行の小さな表なら縞や固定は要りません。列が多い表は、スマホで横にスクロールできる箱に入れます。

この記事の要点

  • 表に縞模様を付ける・スマホで表だけを横にスクロールさせる・見出しの行や左の列をスクロールしても止めておく、の4つがCSSでできる
  • 使いどころは、行や列が多くて比べながら読む表。小さな表には縞も固定も要らない。列が多い表は横スクロールの箱に入れる
  • 見出しを止めるなら border-collapse を collapse にしない。スクロールする箱には tabindex=”0″ と名前を付けて、キーボードでも動かせるようにする
トミー

連載「WEBの技術」の45回目です。
今回は表。見やすくする縞模様と、スマホで右が切れない横スクロールを作ります。

この記事の内容は、無料の道具でも試せます

擬似クラス チェッカー

:nth-child が「どこを選ぶか」が、図で分かる(登録なし)

道具で試してみる →

完成見本:表を見やすくする4つの形

2〜4は、表の箱の中を自動でスクロールさせて、動きが分かるようにしています(見本に触れている間は止まります)。

NTH-CHILD(EVEN)1. 縞模様の表料金表・スペック表など、横に読む行が続く表
OVERFLOW-X: AUTO2. スマホで横にスクロール列が多い表(曜日ごとの時間割、比較表など)
POSITION: STICKY; TOP: 03. 見出しの行を上に固定行が多い表(地域ごとの送料、商品の一覧など)
POSITION: STICKY; LEFT: 04. 左の列を固定して横スクロール横に長く、行の名前を見ながら読む表

※ 見本は読み上げソフトでは読まれないようにしています(コピペ用のコードは、そのまま読み上げられる形です)。動きを減らす設定にしている端末では、自動のスクロールは止まります。

コピペ用のコード

1の「共通の見た目」は、2〜4でも使う土台です。CSSはサイトのCSSファイルや、WordPressならテーマの「追加CSS」などに入れます。HTMLは、ブロックエディタの「カスタムHTML」ブロックに貼ります。クラス名の頭の kd-tb は、サイトのほかのデザインと名前がぶつからないようにするための目印です。

1. 縞模様の表

tbody tr:nth-child(even)(本文の行のうち、偶数番目)の背景だけを、うすい青にしています。行が交互に色分けされるので、横に長い行でも、目で追っている行を見失いにくくなります。金額の列は右にそろえ、font-variant-numeric: tabular-nums(数字の幅をそろえる指定)で、桁の位置も縦にそろえています。

HTML
<table class="kd-tb kd-tb-stripe">
  <caption>更新サポートの料金(月額・税込)</caption>
  <thead>
    <tr><th scope="col">プラン</th><th scope="col">更新できる量</th><th scope="col" class="kd-num">月額</th></tr>
  </thead>
  <tbody>
    <tr><th scope="row">ライト</th><td>5ページまで</td><td class="kd-num">3,300円</td></tr>
    <tr><th scope="row">スタンダード</th><td>10ページまで</td><td class="kd-num">5,500円</td></tr>
    <tr><th scope="row">ビジネス</th><td>20ページまで</td><td class="kd-num">8,800円</td></tr>
    <tr><th scope="row">プレミアム</th><td>ページ数の上限なし</td><td class="kd-num">13,200円</td></tr>
  </tbody>
</table>
CSS(共通の見た目+縞模様)
.kd-tb {
  width: 100%;
  /* 線をセルごとに持たせる(見出しを固定するときに線が消えないように) */
  border-collapse: separate;
  border-spacing: 0;
  font-size: 15px;
  line-height: 1.6;
  color: #10283A;
}
.kd-tb caption {
  /* 表の題名。左寄せで、表の上に置く */
  text-align: left;
  font-weight: 700;
  padding: 0 0 8px;
}
.kd-tb th,
.kd-tb td {
  padding: 10px 14px;
  border-bottom: 1px solid #D8E6F0;
  text-align: left;
  background: #FFFFFF;
}
.kd-tb thead th {
  /* 見出しの行は濃い青に白い文字(明るさの差 約5.9倍) */
  background: #0369A1;
  color: #FFFFFF;
  border-bottom: 0;
}
/* 偶数番目の行だけ、うすい青にする */
.kd-tb-stripe tbody tr:nth-child(even) th,
.kd-tb-stripe tbody tr:nth-child(even) td {
  background: #F2F8FC;
}
/* 金額などの数字は右にそろえ、桁の幅も同じにする */
.kd-tb-stripe .kd-num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
/* プラン名と金額は、途中で折り返さない */
.kd-tb-stripe tbody th,
.kd-tb-stripe .kd-num {
  white-space: nowrap;
}

2. スマホで横にスクロール

表を div の箱で包み、箱に overflow-x: auto(はみ出した分だけ横にスクロールさせる指定)を付けます。表には min-width(幅の下限)を付けて、狭い画面で列がつぶれないようにします。ページ全体は横にずれず、表の箱の中だけが動きます。

HTML
<div class="kd-tb-scroll" role="region" aria-label="店舗ごとの営業時間(横にスクロールできます)" tabindex="0">
  <table class="kd-tb">
    <caption>店舗ごとの営業時間</caption>
    <thead>
      <tr><th scope="col">店舗</th><th scope="col">月</th><th scope="col">火</th><th scope="col">水</th><th scope="col">木</th><th scope="col">金</th><th scope="col">土</th><th scope="col">日</th></tr>
    </thead>
    <tbody>
      <tr><th scope="row">本店</th><td>10:00〜19:00</td><td>10:00〜19:00</td><td>定休日</td><td>10:00〜19:00</td><td>10:00〜20:00</td><td>9:00〜20:00</td><td>9:00〜18:00</td></tr>
      <tr><th scope="row">駅前店</th><td>11:00〜21:00</td><td>11:00〜21:00</td><td>11:00〜21:00</td><td>定休日</td><td>11:00〜22:00</td><td>10:00〜22:00</td><td>10:00〜20:00</td></tr>
      <tr><th scope="row">海辺店</th><td>定休日</td><td>10:00〜18:00</td><td>10:00〜18:00</td><td>10:00〜18:00</td><td>10:00〜18:00</td><td>9:00〜19:00</td><td>9:00〜19:00</td></tr>
    </tbody>
  </table>
</div>
CSS(1の共通の見た目に足す)
.kd-tb-scroll {
  /* 表より画面が狭いときだけ、この箱の中で横にスクロールさせる */
  overflow-x: auto;
}
.kd-tb-scroll .kd-tb {
  /* 表の幅の下限。これより狭い画面では横にスクロールする */
  min-width: 640px;
  /* セルの中で文字を折り返さない */
  white-space: nowrap;
}
/* キーボードで箱を選んだとき、選んでいることが分かる枠 */
.kd-tb-scroll:focus-visible {
  outline: 3px solid #0284C7;
  outline-offset: 2px;
}

3. 見出しの行を上に固定

箱に max-height(高さの上限)と overflow-y: auto を付けて、箱の中を縦にスクロールさせます。見出しのセル(thead th)に position: sticky と top: 0 を付けると、スクロールしても見出しの行が箱の上の端で止まり、いつでも「この列は何か」が見えます。

HTML
<div class="kd-tb-sticky" role="region" aria-label="地域ごとの送料(縦にスクロールできます)" tabindex="0">
  <table class="kd-tb">
    <caption>地域ごとの送料とお届け日数</caption>
    <thead>
      <tr><th scope="col">地域</th><th scope="col">送料</th><th scope="col">お届け日数</th></tr>
    </thead>
    <tbody>
      <tr><th scope="row">北海道</th><td>1,100円</td><td>2〜3日</td></tr>
      <tr><th scope="row">東北</th><td>900円</td><td>1〜2日</td></tr>
      <tr><th scope="row">関東</th><td>800円</td><td>1日</td></tr>
      <tr><th scope="row">中部</th><td>800円</td><td>1日</td></tr>
      <tr><th scope="row">近畿</th><td>800円</td><td>1日</td></tr>
      <tr><th scope="row">中国</th><td>900円</td><td>1〜2日</td></tr>
      <tr><th scope="row">四国</th><td>900円</td><td>1〜2日</td></tr>
      <tr><th scope="row">九州</th><td>1,000円</td><td>2日</td></tr>
      <tr><th scope="row">沖縄</th><td>1,500円</td><td>2〜4日</td></tr>
    </tbody>
  </table>
</div>
CSS(1の共通の見た目に足す)
.kd-tb-sticky {
  /* 箱の高さを決めて、その中で縦にスクロールさせる */
  max-height: 280px;
  overflow-y: auto;
}
.kd-tb-sticky thead th {
  /* 箱の上の端にくっついて止まる */
  position: sticky;
  top: 0;
  /* 下の行より手前に表示する */
  z-index: 1;
}
.kd-tb-sticky:focus-visible {
  outline: 3px solid #0284C7;
  outline-offset: 2px;
}

4. 左の列を固定して横スクロール

2の横スクロールの箱に、クラス kd-tb-firstcol を足します。各行の見出し(tbody th)に position: sticky と left: 0 を付けると、横にスクロールしても左の列だけが止まり、どの行を見ているか分かります。左上の角のセルは、上と左の両方に重なるので、z-index を一段高くしています。

HTML
<div class="kd-tb-scroll" role="region" aria-label="店舗ごとの営業時間(横にスクロールできます)" tabindex="0">
  <table class="kd-tb kd-tb-firstcol">
    <caption>店舗ごとの営業時間</caption>
    <thead>
      <tr><th scope="col">店舗</th><th scope="col">月</th><th scope="col">火</th><th scope="col">水</th><th scope="col">木</th><th scope="col">金</th><th scope="col">土</th><th scope="col">日</th></tr>
    </thead>
    <tbody>
      <tr><th scope="row">本店</th><td>10:00〜19:00</td><td>10:00〜19:00</td><td>定休日</td><td>10:00〜19:00</td><td>10:00〜20:00</td><td>9:00〜20:00</td><td>9:00〜18:00</td></tr>
      <tr><th scope="row">駅前店</th><td>11:00〜21:00</td><td>11:00〜21:00</td><td>11:00〜21:00</td><td>定休日</td><td>11:00〜22:00</td><td>10:00〜22:00</td><td>10:00〜20:00</td></tr>
      <tr><th scope="row">海辺店</th><td>定休日</td><td>10:00〜18:00</td><td>10:00〜18:00</td><td>10:00〜18:00</td><td>10:00〜18:00</td><td>9:00〜19:00</td><td>9:00〜19:00</td></tr>
    </tbody>
  </table>
</div>
CSS(1と2に足す)
/* 2の横スクロールの箱と組み合わせて使う */
.kd-tb-firstcol tbody th {
  /* 各行の見出し(いちばん左の列)を、箱の左の端で止める */
  position: sticky;
  left: 0;
  z-index: 1;
  /* 止まっている列だと分かるように、右側に線を入れる */
  border-right: 2px solid #D8E6F0;
  font-weight: 700;
}
.kd-tb-firstcol thead th:first-child {
  /* 左上の角のセルも止めておく */
  position: sticky;
  left: 0;
  z-index: 2;
}

仕組み:表を「箱」に入れてスクロールさせる

ページではなく、箱の中だけを動かす

表がスマホの画面より広いと、そのままではページ全体が横にずれたり、右側が切れて見えなくなったりします。2では、表を div の箱で包み、箱のほうに overflow-x: auto を付けています。はみ出した分だけ、その箱の中で横にスクロールできるようになり、ページの本文はずれません。

MDNの表の説明でも、大きな表を狭い場所に収める方法として、overflow: auto でスクロールさせる例が紹介されています。

sticky は「いちばん近いスクロールの箱」で止まる

position: sticky は、ふだんはふつうの位置にあり、スクロールして決めた位置(top: 0 なら上の端)に来ると、そこで止まる指定です。MDNによると、止まる基準は、いちばん近い「スクロールのしくみを持つ祖先」です。overflow に auto や hidden を付けた箱があると、その箱が基準になります。

3では、高さを決めた箱の中で見出しが止まります。4は、横にスクロールする箱の中で、左の列が止まります。逆に言うと、表を overflow 付きの箱に入れたまま、ページ全体のスクロールで見出しを止めることはできません。止めたいほうのスクロールを、箱の中にそろえる必要があります。

見出しを止めるなら、線はセルごとに

表の線の引き方には、隣のセルと線を共有する collapse と、セルごとに線を持つ separate があります。MDNの表の説明では、見出しを sticky で止めるときは border-collapse を collapse にしないよう書かれています。共有している線は、止まった見出しといっしょに動かないからです。

WordPressのテーマには、表に border-collapse: collapse を付けているものが多くあります。見本のCSSでは separate と border-spacing: 0(セルのすき間を0に)を指定し、線は各セルの下の辺(border-bottom)に引いています。

caption と th で、表の意味を伝える

<caption> は表の題名です。MDNによると、caption は表の名前として読み上げソフトに伝わり、その表を読むか飛ばすかを決める手がかりになります。見出しのセルは <td> ではなく <th> で書き、列の見出しには scope="col"、行の見出しには scope="row" を付けています。簡単な表では省いても伝わりますが、MDNでは、付けたほうが読み上げソフトで確実に伝わると説明されています。

数値を変えるとどうなるか

セルの余白(1)

余白 3px・6px:詰まりすぎ
余白 9px・12px(見本)
余白 16px・18px:広すぎ

余白が少ないと(左)、行と行が近すぎて、どの数字がどの行のものか迷いやすくなります。広すぎると(右)、1画面に入る行が減り、スマホではスクロールが長くなります。見本は上下 9〜10px、左右 12〜14px です。

縞の濃さ(1)

縞なし
#F2F8FC(見本)
#7DD3FC:濃すぎ

縞は、行を区別できる程度のうすさで十分です。濃い色にすると(右)、縞のほうが目立って、色の付いた行が「大事な行」のように見えてしまいます。強調したい行があるなら、縞とは別の方法(文字を太くする、ラベルを付けるなど)で示します。

幅の下限と、折り返し(2)

幅の下限なし・折り返しあり
min-width: 640px・折り返さない(見本)

min-width を付けずに狭い画面に置くと(左)、ブラウザが列を細くして、時間が「10:00〜」と「19:00」の2行に分かれてしまいます。表の幅に下限を付けて、white-space: nowrap(文字を折り返さない)を指定すると(右)、1つのセルが1行のまま読めます。

変えるところ指定今回の値目安
セルの余白padding10px 14px文字の大きさの0.6〜1倍くらい
縞の色background#F2F8FC白との差がうっすら分かる程度
表の幅の下限min-width640px1つのセルが1行で読める幅
縦にスクロールする高さmax-height280pxスマホの画面の半分くらいまで

スマホと、見る人への配慮

スクロールできる箱は、キーボードでも動かせるように

マウスやタッチなら箱の中を動かせますが、キーボードだけで操作している人は、箱を選べないと中を見られません。コピペ用のHTMLでは、箱に tabindex="0" を付けて、Tabキーで選べるようにしています。選んだあとは、矢印キーで中をスクロールできます。選んでいることが分かるように、:focus-visible で青い枠を出しています。

あわせて、role="region"(ひとまとまりの場所)と aria-label(その場所の名前)を付けています。Tabキーで箱に来たとき、読み上げソフトが「何の箱か」を伝えられるようにするためです。

「横に動かせる」ことが分かるように

スマホでは、表の右が切れていても、横に動かせることに気づかない人がいます。見本のように、右端の列が途中で切れて見えると、続きがあることが伝わりやすくなります。それでも伝わりにくい表なら、表の上に「横にスクロールできます」と一言そえます。

色だけで意味を持たせない

縞の色は、行を見分けやすくするための飾りです。強制カラーモード(画面の色を決まった配色に変える設定)では背景の色が置きかわるので、縞は見えなくなることがあります。そのときも読めるように、行の下には線(border-bottom)を残しています。見出しの行の白い文字と濃い青(#0369A1)は、明るさの差が約5.9倍です。

やりすぎ注意

縦と横の両方にスクロールする箱は、スマホでは指の動きが思った方向に行かず、操作しにくくなります。1つの表で使うスクロールは、横か縦のどちらかにしぼるのがおすすめです。また、ページの中にスクロールする箱がいくつも並ぶと、ページを下に進めたいのに箱の中だけが動く、ということが起きやすくなります。

トミー

表は「全部見せる」より「比べやすくする」ための部品です。
列が多すぎるなら、横スクロールの前に、列を減らせないかも考えます。

AIに頼むなら

表のCSSも、Claudeなどの生成AIに頼むとすぐ作ってもらえます。そのときは、表の中身(何行・何列か)と、スマホでどう見せたいかまで伝えると、使えるコードになりやすくなります。

8列ある営業時間の表を、スマホでは表だけ横にスクロールできるようにして。
左の店舗名の列は止めて、caption と th scope を使い、
スクロールする箱は tabindex="0" と aria-label でキーボードでも動かせるようにして。

AIが出したコードは、そのまま使う前に、次の3つを確かめます。

  • スマホ幅で、ページ全体が横にずれないか。overflow-x: auto が表ではなく、表を包む箱に付いているかを見ます
  • 見出しを止める表に border-collapse: collapse が残っていないか。テーマ側の指定が効いている場合もあるので、実際にスクロールして線が残るか確かめます
  • 見出しのセルが th になっているか。見た目だけ太字にした td だと、読み上げソフトに見出しとして伝わりません

KAKERUならこう使う/使わない

最後に、作り手としての判断です。

使う場面

  • 料金表やプランの比較。1の縞模様と、数字の右そろえで、行ごとの金額を比べやすくします
  • 曜日ごとの営業時間や、スペックの比較のように列が多い表。2の横スクロールに、4の左の列の固定を組み合わせます
  • 送料や商品の一覧のように、行が20を超える表。3で見出しを止めると、下のほうでも列の意味が分かります

使わない場面

  • 3〜4行の小さな表。縞も固定も要りません。線だけで十分読めます
  • 見た目を横に並べたいだけのレイアウト。カードの並びなどに表を使うと、読み上げソフトに「表」として伝わってしまいます。並べるのは CSS のグリッドで行います
  • スマホで見る人が多いのに、列が10を超える表。横スクロールでも読みにくいので、表を分けるか、項目ごとのカードに作り直します
トミー

KAKERUなら、まず「この表、列を減らせないか」を考えます。
それでも多いときに、横スクロールと左の列の固定をセットで入れます。

まとめ

  • 縞模様は tbody tr:nth-child(even) の背景で。うすい色で十分、行の下の線も残す
  • スマホで右が切れる表は、div の箱で包み、箱に overflow-x: auto、表に min-width
  • 見出しの行は thead th に position: sticky と top: 0。左の列は tbody th に left: 0
  • sticky は「いちばん近いスクロールの箱」で止まる。線は border-collapse: separate でセルごとに
  • スクロールする箱には tabindex="0"・role="region"・aria-label。caption と th scope で表の意味を伝える

まずは1のコードをコピーして、サイトの料金表で試してみてください。次に、スマホ幅で右が切れている表があれば、2の箱で包むだけで読めるようになります。

参考にした資料

いずれも2026年11月5日に確認しました。ブラウザの対応状況は変わることがあります。最新の情報は各ページでご確認ください。

ふだんの制作の話や、作ったものの裏側は Instagram と X でも出しています。

関連記事

ヘッダーを上に固定するCSS。position: stickyが止まらない原因と、使う・使わないの判断
カードを並べるCSS。グリッドで自動で折り返す、auto-fillとauto-fitの違いと使う・使わないの判断
画面の幅で文字の大きさを変えるCSS clamp() の式の作り方と、使う・使わないの判断
区切り線をCSSで描きかえる。破線・グラデーション線・記号つき・波線の見本とコード
表をCSSで見やすく。縞模様・横スクロール・見出しと左の列の固定の見本とコード

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

CAPTCHA


ABOUTこの記事をかいた人

25歳からWEBの世界に携わり、WEBデザインから始めて、今はWEBシステムまで一貫して制作しています。問題点を洗い出し、改善を提案する中で、WEB・グラフィック・動画・システムを縦断して提案するのが得意です。