References

Beginner-friendly references for web development, with live, editable examples.

The HTML aria-colcount attribute

ARIA Accessibility
In short

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

Live 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 row and rowgroup roles 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.

Frequently asked questions

Do columns the user has hidden still count toward aria-colcount?
Yes, a hidden column stays in aria-colcount if it still belongs to the table. The value has to equal the number of columns in the full table.
Does aria-colcount remove columns from view?
No, aria-colcount changes nothing on screen. Your own code or CSS decides which columns appear.
What value does aria-colcount take when the column total is unknown?
Use -1 for aria-colcount when the total is unknown. It fits a table whose columns you know won't all be in the DOM.
Is aria-colcount allowed on a native table?
Yes, aria-colcount is allowed on a table element, because its implicit role is table.
Is aria-colindex required alongside aria-colcount?
Yes, once columns are missing from the DOM, aria-colcount needs aria-colindex as well. Without aria-colindex, cells report positions counted from the columns in the DOM.