The HTML aria-colcount attribute
A table with columns missing from the DOM uses aria-colcount to state how many columns it has in full. A native table and a grid built from roles both carry it on the outer element, as in aria-colcount="6", and -1 marks a total you don't know.
Overview
The aria-colcount attribute is your statement of how many columns a table has, including any missing from the DOM. You might leave some out because the table is too large to show them all, or because a table that wide could make for a bad user experience.
With every column in the DOM, you can skip it, because the browser can calculate the total. Once columns are missing, the attribute is needed. A report showing columns 1, 2, 5 and 6 of 6 sets aria-colcount="6" on the table and aria-colindex on its cells.
A column hidden with display: none behaves much the same. Its cells stay in the DOM, but Chrome and Firefox leave them out of the accessibility tree and renumber the columns that remain. With aria-colcount and aria-colindex set, both browsers expose the real numbers instead.
A report with one column per customer, built from data that loads later, may not know its width up front. That case calls for -1, the required value that tells the browser it shouldn't calculate one.
HTML has no attribute for a column total, and <table> takes only global attributes.
Syntax
<table aria-colcount="6" aria-label="Contacts">
<tr>
<th scope="col" aria-colindex="1">Name</th>
<th scope="col" aria-colindex="5">City</th>
</tr>
</table>
Values
| Value |
|---|
| An integer (use -1 if the total is unknown). |
Example
<p style="margin: 0 0 6px">Check a box to put that column in the DOM.</p>
<p style="margin: 0 0 6px">
<label><input type="checkbox" id="size"> Size</label>
<label><input type="checkbox" id="color"> Color</label>
</p>
<table id="stock" aria-colcount="6" aria-label="Product stock" style="border-collapse: collapse">
<thead id="head"></thead>
<tbody id="rows"></tbody>
</table>
<p role="status" id="state" style="margin: 6px 0 0"></p>
<script>
const columns = ['SKU', 'Item', 'Size', 'Color', 'Stock', 'Price'];
const items = [
['A12', 'Mug', 'Large', 'Blue', '14', '$9'],
['B07', 'Cap', 'Small', 'Red', '6', '$12'],
['C33', 'Bag', 'Medium', 'Green', '3', '$25']
];
const head = document.getElementById('head');
const rows = document.getElementById('rows');
const state = document.getElementById('state');
const size = document.getElementById('size');
const color = document.getElementById('color');
function addCell(row, tag, text, column) {
const cell = document.createElement(tag);
cell.textContent = text;
cell.setAttribute('aria-colindex', column);
cell.style.padding = '2px';
if (tag === 'th') cell.scope = 'col';
row.appendChild(cell);
}
function render() {
const shown = columns.map((name, i) => i).filter(i => (i !== 2 || size.checked) && (i !== 3 || color.checked));
head.replaceChildren();
rows.replaceChildren();
const top = head.insertRow();
shown.forEach(i => addCell(top, 'th', columns[i], i + 1));
items.forEach(item => {
const row = rows.insertRow();
shown.forEach(i => addCell(row, 'td', item[i], i + 1));
});
state.textContent = shown.length === 6
? 'All 6 columns are in the DOM, so aria-colcount could be dropped.'
: shown.length + ' of 6 columns are in the DOM. aria-colcount stays 6, and Price keeps aria-colindex 6.';
}
size.addEventListener('change', render);
color.addEventListener('change', render);
render();
</script>
Best practices
- Keep aria-colcount fixed while columns come and go. A column picker changes what's in the DOM, not the size of the full table.
- Count columns, not cells. A row with a spanning cell holds fewer cells than the table has columns, and the total covers them all.
- Put the attribute on the element with the table, grid or treegrid role. The
rowandrowgrouproles don't support it. - Prefer a native <table>, which needs less markup than elements with roles. A native table with columns missing still needs aria-colcount.























