Matterhorn 15-003 · PDF/UA-1 UA1:7.5-2
TH cells need a Scope attribute — or Headers and IDs
Header cells must declare whether they head a row, a column, or both, unless the table instead uses explicit /Headers ID references on every data cell.
The condition, as the protocol states it. “In a table not organized with Headers attributes and IDs, a <TH> cell does not contain a Scope attribute.”
Matterhorn Protocol 1.1, checkpoint 15, index 15-003, section UA1:7.5-2. Software can decide this one on its own.
What a screen reader actually does
The reader guesses the association, and guesses wrong on any table more complex than a simple grid.
What is actually in the file
A table cell is a structure element like any other. What makes it a header is its type, and what makes the header useful is an attribute:
24 0 obj
<< /Type /StructElem
/S /TH
/P 23 0 R
/Pg 3 0 R
/A << /O /Table /Scope /Column >>
/K [ 11 ]
>>
endobj
The /A entry is the attribute dictionary. /O /Table declares the attribute owner — the namespace the attribute belongs to — and /Scope says what this header governs. Without it the element is still a TH, but a processor has no way to know whether it labels the cells below it or the cells beside it.
PDF/UA-1 clause 7.5 gives you two mutually exclusive ways to express header associations:
/Scopeon eachTH. Simple, and sufficient for a regular grid./Headerson each data cell, holding an array of/IDstrings that name the header cells governing it. Verbose, and the only option once the table is irregular.
Condition 15-003 fires when a table uses neither: a TH with no /Scope, in a table where no cell carries /Headers.
What goes wrong without it
Screen readers have to guess, and their guesses differ. JAWS, NVDA and VoiceOver each apply their own fallback — usually “first row is column headers, first column is row headers” — and each gets a different answer on anything that is not a plain grid.
On a simple table you often get away with it. On a table with a stub column, a totals row, or a second header row, the reader announces confidently wrong headers, which is worse than announcing none: the user has no signal that anything is off.
How to fix it free, in Acrobat Pro
Acrobat can do this, through a dialog most people never find.
- Open the Tags panel and find the
<Table>element. - Right-click the table → Table Editor. (If it is greyed out, the table structure itself is broken — fix that first; that is condition 09-004.)
- The page now shows a coloured grid over the table. Red cells are
TH, grey areTD. - Any header cell showing grey: right-click it → Table Cell Properties → set Type to Header Cell.
- In the same dialog, set Scope to Row, Column or Both.
- Repeat for every header cell. You can shift-click a run of cells in one row and set the scope for all of them at once — this is the one place Acrobat gives you multi-select, and it saves real time.
- Close the Table Editor and re-run
Accessibility → Accessibility Check.
The faster free route, if you still have the source: in Word, select the header row and tick Table Tools → Layout → Repeat Header Rows, and in the table properties tick Repeat as header row at the top of each page. Word then exports the row as TH with /Scope /Column already set. In InDesign, convert the row to a header row in Table → Convert Rows → To Header. Doing it upstream takes seconds and survives the next revision.
Where Scope stops being enough
Consider a table whose first header row spans years and whose second row splits each year into quarters:
| 2025 | 2025 | 2026 | 2026 | |
|---|---|---|---|---|
| Region | Q1 | Q2 | Q1 | Q2 |
| North | 1 | 2 | 3 | 4 |
The cell containing 3 is headed by North, by Q1, and by 2026. /Scope cannot say that: it has no way to express “the 2026 header governs columns 4 and 5 only”. Every data cell needs /Headers:
<< /Type /StructElem /S /TD
/A << /O /Table /Headers [ (north) (q1b) (y2026) ] >>
>>
and each header cell needs a matching /ID. Acrobat’s Table Editor exposes this through Table Cell Properties → Associated Header Cell IDs, one cell at a time. On a 5×20 table that is 100 dialogs.
Where Taggart differs
Taggart reports the two cases separately, because they need different work.
For 15-003 it names every TH missing a /Scope, and the fix is mechanical: Taggart infers Row or Column from the cell’s position — a cell in an all-TH row heads its column, a leading cell in a body row heads its row, a corner cell in a stacked header gets Both — and writes the attribute with the correct /O /Table owner. It ships in “Fix All Safe”.
For 15-005 it detects the tables where /Scope is genuinely insufficient — spanning cells or stacked header rows — and says so, rather than writing a /Scope that would look conformant and still mislead a reader.
How Taggart handles it
Taggart detects this condition automatically. It is reported as a blocker, with the page, the structure path and the object id of every occurrence.
The fix is Set table headers, Scope and Headers references. It changes what the document means, so Taggart proposes it and waits for your approval rather than applying it unattended.
Whatever Taggart changes, the page still looks identical. Fixes edit the PDF object model, not the content streams, and every remediation is re-rendered and compared against the original at structural similarity ≥ 0.999 before you get the file back. A tool that quietly reflows your document is worse than no tool.
Frequently asked questions
Do I need Scope on every TH, or only the first row?
Every TH, unless the table uses Headers and IDs instead. The protocol's condition is per-cell: any TH in a table not organised with Headers references and lacking a Scope attribute is a failure. In practice that means the whole header row and, in most data tables, the first cell of every body row too.
What are the valid values?
Row, Column and Both. Nothing else. /Scope /None, /Scope /Disc and similar values are failures — they usually mean the producer wrote a placeholder. Both is for a corner cell that heads its row and its column at once.
When should I use Headers and IDs instead of Scope?
As soon as the table has merged cells or more than one header row. Scope says 'this cell heads its row' or 'its column' and nothing more, which cannot express a header that governs three of five columns. At that point every data cell needs an explicit /Headers array of /ID references. That is condition 15-005.
Does this apply to layout tables?
It should not, because content laid out in a grid for appearance should not be tagged as a table at all — that is condition 15-004. If you have a layout table, the fix is to retag it as ordinary paragraphs and containers, not to add Scope to it.
Where this sits in the standards
| Standard | Reference |
|---|---|
| Matterhorn Protocol 1.1 | Checkpoint 15 (Tables), index 15-003 |
| PDF/UA-1 (ISO 14289-1) | Clause 7.5-2 |
| WCAG 2.1 | 1.3.1 Info and Relationships — Level A |
| EN 301 549 / Section 508 / ADA Title II | All three point at WCAG 2.1 Level AA for non-web documents, so a Level A or AA criterion here is in scope for each of them. |
Related conditions
- 15-001 A row has a header cell, but that header cell is not tagged as a header The leftmost (or, in RTL, rightmost) cell of each row labels the row but is tagged TD instead of TH.
- 15-002 A column has a header cell, but that header cell is not tagged as a header The top row labels the columns but is tagged as ordinary data cells.
- 15-005 A given cell's header cannot be unambiguously determined In a complex table — merged cells, stacked header rows, split spans — Scope alone cannot express the associations, so explicit /Headers references are required.
- 09-004 A table-related structure element does not conform to ISO 32000-1, Table 337 The table grammar is broken: a TD outside a TR, a TR outside Table/THead/TBody/TFoot, a Table with no rows.
Other conditions in checkpoint 15
Checkpoint 15 covers tables. These are its other failure conditions; the ones without a link are in the rule set but do not have a written page yet.
- 15-001 A row has a header cell, but that header cell is not tagged as a header The leftmost (or, in RTL, rightmost) cell of each row labels the row but is tagged TD instead of TH.
- 15-002 A column has a header cell, but that header cell is not tagged as a header The top row labels the columns but is tagged as ordinary data cells.
- 15-004 Content is tagged as a table for information not organized in rows and columns A table used purely to position content on the page — a two-column layout, a form arrangement — has been tagged as a real table.
- 15-005 A given cell's header cannot be unambiguously determined In a complex table — merged cells, stacked header rows, split spans — Scope alone cannot express the associations, so explicit /Headers references are required.
Check your own file. Taggart’s validator is free and unlimited — every machine-checkable Matterhorn condition, no page cap, no watermark, no account needed.
Verified against a real document on . Last updated .