<th>
and
<td>,
and why does that difference matter beyond visual styling?<th>
is a header cell — it names a column or row. Browsers and screen readers use it to announce which column or row a data cell belongs to, so users always know the context of what they are reading.
<td>
is a data cell — it holds the actual values. Using
<td>
for headers removes that context, leaving screen reader users to hear a stream of numbers with no labels attached.
<table>.
Inside it, rows are created with
<tr>
(table row). Inside rows, cells are either
<th>
(header cell) or
<td>
(data cell). Every row must have the same number of cells — the browser does not guess about missing ones, and a missing cell silently shifts every subsequent column in that row left.<thead>
wraps the header rows,
<tbody>
wraps the data rows, and
<tfoot>
wraps totals or summary rows. These are not just organisational — browsers use them to repeat the header on each printed page, and screen readers use them to orient users as they navigate cell by cell through a large table.scope
attribute on
<th>
is what connects a header to the cells it labels.
scope="col"
means this cell is the header for everything below it in its column.
scope="row"
means this cell is the header for everything to its right in the same row. Without
scope,
a screen reader navigating cell by cell reads out values with no context about what those values mean.<caption> — This is the table's title. It goes immediately inside the opening
<table>
tag and is displayed above the table by default. It is the first thing a screen reader announces when a user enters the table — without it, a user must read several cells before understanding what the table is about. Never substitute a
<p>
or
<h3>
sitting visually above the table — those elements are not programmatically linked to it.
<thead>,
<tbody>,
<tfoot> — These three sections divide the table into named regions. Even if you only use
<tbody>,
including it is worth doing because browsers treat rows inside
<tbody>
differently when printing long tables — the
<thead>
rows are automatically repeated at the top of every printed page. The
<tfoot>
is where totals, averages, or summaries belong, and browsers may also repeat it at the bottom of each printed page.
scope="col"
vs
scope="row" — The rule is straightforward: every
<th>
in
<thead>
gets
scope="col"
because it heads a column of data below it. Every
<th>
in the first cell of a data or footer row gets
scope="row"
because it labels the data cells to its right. With both in place, a screen reader navigating to any cell can announce: "North, February — $15,200" instead of just "$15,200".
<td></td>
or
<td>—</td>
anyway. Every row must have exactly as many cells as the header row declares (accounting for any colspan or rowspan). Skipping a cell shifts every subsequent cell in that row one column to the left — data lands in the wrong column silently, with no visible error.
colspan
and
rowspan — When one cell must span two or more columns, add
colspan="2"
to that cell and remove the cells it replaces from the same row. For a cell spanning multiple rows, use
rowspan="2"
and remove the covered cells from the rows below. Always count carefully — one missing or extra cell corrupts the entire column alignment from that point on.
scope
is correct, each cell is announced with its column and row headers. Without it, cells are read as isolated values — numbers with no meaning attached.<thead>
at the top of each page. Without a
<thead>,
only the first page shows column labels — every other page is unlabelled data.border="1",
cellpadding="5",
and
bgcolor
directly on the table element. These attributes are deprecated. All visual styling belongs in CSS — the HTML describes the data structure only.<caption>
and missing
scope
values. Be cautious if it suggests adding
role="table"
to a
<table>
element — that role is already built in and adding it is redundant.
schedule.html.
Build a weekly class schedule: rows are time slots (9am, 10am, 11am), columns are weekdays (Mon–Fri). Add a
<caption>
naming the table clearly.<thead>
with a header row of
<th scope="col">
cells for each weekday. Add
<tbody>
with three rows. Make the first cell in each data row a
<th scope="row">
showing the time slot.<td>—</td>
— never skip a cell. Skipping even one cell shifts everything in that row left silently.<tfoot>
row showing how many classes are scheduled each day. Use
<th scope="row">Classes</th>
for the label and
<td>
for each count.scope
attributes and notice how the accessibility tree changes.<td>
for header cells. Bold CSS can make a
<td>
look like a header visually, but the semantic link to the data cells it should label is gone. Screen readers cannot announce "you are in column: January" when there is no
<th>
to reference.<td>
shifts every subsequent cell in that row one column to the left — wrong data in wrong columns, no error shown.<caption>.
An
<h3>
sitting visually above the table looks fine but is not programmatically connected to it. When a screen reader jumps directly to the table landmark, it finds no title. Only
<caption>
is genuinely part of the table.<table>
causes screen readers to navigate it cell by cell, mixing sidebar and main content in an unpredictable order. Use CSS Flexbox or Grid for layout — tables are exclusively for tabular data.border="1",
cellpadding="5",
bgcolor="#eee".
These are all deprecated. Everything visual belongs in CSS. The HTML table element describes data structure only.<caption>,
<thead>,
<tbody>,
<tfoot>,
and correct
scope
attributes on every header cell — and explain why tables are for tabular data only, never for page layout.
Welcome back to Jit4All