HTML
08 / 12

Tables & Lists

HTML Tables & Lists

Accessible Table Structure

<table>
  <caption>Q4 2024 Sales by Region</caption>  <!-- table title for screen readers -->

  <thead>
    <tr>
      <th scope="col">Region</th>
      <th scope="col">Q3</th>
      <th scope="col" aria-sort="descending">Q4</th>  <!-- sorted column -->
      <th scope="col">Change</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <th scope="row">Europe</th>   <!-- row header -->
      <td>$1.2M</td>
      <td>$1.8M</td>
      <td><span aria-label="up 50%">↑ 50%</span></td>
    </tr>
    <tr>
      <th scope="row">Americas</th>
      <td>$2.1M</td>
      <td>$2.4M</td>
      <td><span aria-label="up 14%">↑ 14%</span></td>
    </tr>
  </tbody>

  <tfoot>
    <tr>
      <th scope="row">Total</th>
      <td>$3.3M</td>
      <td>$4.2M</td>
      <td><span aria-label="up 27%">↑ 27%</span></td>
    </tr>
  </tfoot>
</table>

Spanning Cells

<table>
  <tr>
    <th colspan="3">2024 Summary</th>  <!-- spans 3 columns -->
  </tr>
  <tr>
    <th>Q1</th>
    <th>Q2</th>
    <th>Q3</th>
  </tr>
  <tr>
    <td rowspan="2">$1M</td>  <!-- spans 2 rows -->
    <td>$1.2M</td>
    <td>$1.4M</td>
  </tr>
  <tr>
    <!-- first cell is occupied by rowspan above -->
    <td>$1.3M</td>
    <td>$1.5M</td>
  </tr>
</table>

Complex Tables with headers attribute

<!-- For complex tables where scope is ambiguous -->
<table>
  <tr>
    <td></td>
    <th id="mon">Monday</th>
    <th id="tue">Tuesday</th>
  </tr>
  <tr>
    <th id="morning">Morning</th>
    <td headers="mon morning">9am meeting</td>
    <td headers="tue morning">Code review</td>
  </tr>
  <tr>
    <th id="afternoon">Afternoon</th>
    <td headers="mon afternoon">Sprint planning</td>
    <td headers="tue afternoon">1:1</td>
  </tr>
</table>

Lists

<!-- Unordered list — bullet points -->
<ul>
  <li>Item one</li>
  <li>Item two
    <ul>
      <li>Nested item</li>
    </ul>
  </li>
</ul>

<!-- Ordered list — numbered -->
<ol>
  <li>First step</li>
  <li>Second step</li>
</ol>

<!-- Ordered list attributes -->
<ol type="A">    <!-- A, B, C ... -->
<ol type="i">    <!-- i, ii, iii ... -->
<ol start="5">   <!-- start from 5 -->
<ol reversed>    <!-- count down -->

<!-- Definition list — term and definition pairs -->
<dl>
  <dt>HTML</dt>
  <dd>HyperText Markup Language — the structure of web pages</dd>

  <dt>CSS</dt>
  <dd>Cascading Style Sheets — the presentation layer</dd>

  <dt>HTTP</dt>
  <dt>HTTPS</dt>  <!-- multiple terms can share a definition -->
  <dd>Protocol for web communication</dd>
</dl>

Table Accessibility Checklist

  • Always include <caption> or aria-label describing the table's purpose

  • Use <th> with scope="col" for column headers and scope="row" for row headers

  • Use <thead>, <tbody>, <tfoot> to structure the table semantically

  • Avoid using tables for layout — use CSS Grid or Flexbox instead

  • Don't use empty <th> cells — use aria-hidden="true" if truly needed

  • Test with screen reader (NVDA/VoiceOver) — tables are announced differently by different readers

  • Avoid complex merged cells when simpler layout would work

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free