Loading Developer Playground

Loading ...

Skip to main content
ARIA ATTRIBUTERelationship Attributes

aria-colcount

Defines the total number of columns in a table, grid, or treegrid when not all columns are present in the DOM.

Value Type
Integer
Used With
table, grid, treegrid
Special Value
-1 = unknown count

Overview

The aria-colcount attribute defines the total number of columns in a table, grid, or treegrid. It's essential when implementing virtual scrolling or when only a subset of columns are rendered in the DOM.

When all columns are present in the DOM, browsers can calculate the column count automatically. However, for performance optimization with large datasets, you may only render visible columns. In these cases, aria-colcount tells assistive technologies the true total.

Use aria-colcount="-1" when the total number of columns is unknown (e.g., dynamically loaded data).

Live Demo: Virtual Table with Column Visibility

Toggle columns to show/hide (total: 8 columns):

ID(col 1)Name(col 2)Email(col 3)Phone(col 4)
1Alice Johnsonalice@example.com555-0101
2Bob Smithbob@example.com555-0102
3Carol Williamscarol@example.com555-0103

aria-colcount="8" tells screen readers there are 8 total columns, even though only 4 are currently visible. Each cell uses aria-colindex to indicate its position.

Code Examples

Basic Usage

<!-- aria-colcount indicates total columns when not all are in DOM -->

<!-- Table with 8 total columns, only 4 visible -->
<table role="grid" aria-colcount="8" aria-label="Employee directory">
  <thead>
    <tr>
      <th aria-colindex="1">ID</th>
      <th aria-colindex="2">Name</th>
      <th aria-colindex="3">Email</th>
      <th aria-colindex="5">Department</th>
      <!-- Columns 4, 6, 7, 8 not rendered but exist -->
    </tr>
  </thead>
  <tbody>
    <tr>
      <td aria-colindex="1">1</td>
      <td aria-colindex="2">Alice Johnson</td>
      <td aria-colindex="3">alice@example.com</td>
      <td aria-colindex="5">Engineering</td>
    </tr>
  </tbody>
</table>

<!-- Screen reader announces: "Table with 8 columns" -->

Virtual Scrolling Grid

<!-- Virtual scrolling grid with many columns -->

<div 
  role="grid" 
  aria-colcount="100"
  aria-rowcount="1000"
  aria-label="Spreadsheet"
>
  <!-- Only render visible viewport -->
  <div role="row" aria-rowindex="1">
    <div role="columnheader" aria-colindex="1">A</div>
    <div role="columnheader" aria-colindex="2">B</div>
    <div role="columnheader" aria-colindex="3">C</div>
    <!-- ... columns D through CV not in DOM ... -->
  </div>
  
  <div role="row" aria-rowindex="5">
    <div role="gridcell" aria-colindex="1">Data A5</div>
    <div role="gridcell" aria-colindex="2">Data B5</div>
    <div role="gridcell" aria-colindex="3">Data C5</div>
  </div>
</div>

<!-- aria-colcount="100" tells AT there are 100 columns total -->

Unknown Column Count

<!-- When total column count is unknown -->

<!-- Use aria-colcount="-1" for dynamic/unknown totals -->
<table role="grid" aria-colcount="-1" aria-label="Dynamic data">
  <thead>
    <tr>
      <th aria-colindex="1">Column 1</th>
      <th aria-colindex="2">Column 2</th>
      <!-- More columns load dynamically -->
    </tr>
  </thead>
</table>

<!-- -1 indicates "count unknown" to assistive technology -->

React Component

// React virtual table with aria-colcount
import { useState, useMemo } from 'react';

interface Column {
  id: string;
  label: string;
  width: number;
}

interface VirtualTableProps {
  columns: Column[];
  data: Record<string, string>[];
  visibleColumnIndices: number[];
  label: string;
}

function VirtualTable({ 
  columns, 
  data, 
  visibleColumnIndices,
  label 
}: VirtualTableProps) {
  const totalColumns = columns.length;
  
  const visibleColumns = useMemo(() => 
    visibleColumnIndices.map(i => ({
      ...columns[i],
      originalIndex: i + 1 // 1-based for aria-colindex
    })),
    [columns, visibleColumnIndices]
  );

  return (
    <table
      role="grid"
      aria-colcount={totalColumns}
      aria-label={label}
    >
      <thead>
        <tr>
          {visibleColumns.map(col => (
            <th
              key={col.id}
              aria-colindex={col.originalIndex}
              scope="col"
            >
              {col.label}
            </th>
          ))}
        </tr>
      </thead>
      <tbody>
        {data.map((row, rowIndex) => (
          <tr key={rowIndex}>
            {visibleColumns.map(col => (
              <td 
                key={col.id}
                aria-colindex={col.originalIndex}
              >
                {row[col.id]}
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

// Usage
function App() {
  const columns = [
    { id: 'id', label: 'ID', width: 50 },
    { id: 'name', label: 'Name', width: 150 },
    { id: 'email', label: 'Email', width: 200 },
    { id: 'phone', label: 'Phone', width: 120 },
    { id: 'dept', label: 'Department', width: 120 },
  ];
  
  const [visibleCols, setVisibleCols] = useState([0, 1, 2]);
  
  return (
    <VirtualTable
      columns={columns}
      data={data}
      visibleColumnIndices={visibleCols}
      label="Employee directory"
    />
  );
}

Best Practices

Do

  • • Use when columns are virtualized or hidden
  • • Always pair with aria-colindex on cells
  • • Use -1 when count is truly unknown
  • • Update count when columns are added/removed
  • • Apply to table, grid, or treegrid elements

Don't

  • • Use when all columns are in the DOM
  • • Forget aria-colindex on individual cells
  • • Use 0 for unknown (use -1 instead)
  • • Apply to non-table/grid elements
  • • Mismatch count with actual total columns

Related Attributes

Specifications & Resources