scalpel@labs: ~/glossary/web-table-detection.mdx5 sections

Web Table Detection: Real Tables, ARIA Grids and Div Grids

Web table detection is the job of finding tabular data in a page's markup. Some tables use the real `<table>` element; many use `<div>`s styled as grids, ARIA roles, or repeated cards. A good extractor recognises all four shapes, not just `<table>`.

extension: Scalpel Tablesupdated: 2026-08-14read_time: 3 min
less web-table-detection.mdx

Why it matters

Not every table on the web is a real <table> element. A modern design system might build a grid with CSS and divs. An old email template might use tables for layout (and nest data tables inside). A product list might repeat the same card structure dozens of times. To audit or extract data, you need to recognise all four shapes.

An extractor that only looks for <table> will miss 70% of the grids on a typical modern site. That means incomplete audits, lost data, and manual workarounds that waste time. A detector that reads all four finds the data where it actually is.

How it works

The detector scans the page's DOM and tests each candidate node against four patterns, in priority order.

Real tables. The simplest case: <table> elements with <tbody>, <thead>, or <tr> children. The markup declares the structure. Rows and columns are explicit. Row headers, column headers, and cell merges are all marked up.

ARIA grids. An element with role="table" or role="grid" signals tabular data even if it uses divs under the hood. ARIA attributes like aria-rowindex, aria-colindex, and aria-colspan describe the structure. The role is how screen readers know it's a grid; the detector uses it to confirm.

CSS grids and table layouts. CSS display: grid, display: table, or display: table-cell styles elements as grids without changing the markup. The detector walks the DOM hierarchy, inspects computed styles, and infers rows and columns by layout boundaries. A <div class="row"> inside a display: grid parent is recognised as a row, even though the markup doesn't say so.

Repeated card structures. A list of identical or near-identical sibling elements, each with the same internal structure, can be read as table rows. The detector counts repeating patterns: if every sibling has an identical child tree (e.g. <div class="product-card"> with a <h2>, <p>, <span> in every copy), it's probably a grid of records. Each child's content becomes a column.

Priority prevents duplication. A real <table> wins over a div guess for the same node. A detected <table> takes precedence over an ARIA grid overlay of the same content.

What does not matter

The HTML elements used. A table doesn't need <table>. Divs with grid styling work just as well from a data perspective, and they're more flexible for responsive design. Don't assume <table> is better. Much modern markup is intentionally div-based.

Perfect cell alignment. A ragged grid (rows of different lengths) is valid. The detector pads shorter rows with empty cells to keep the output rectangular, but the input doesn't need to be.

Semantic correctness. An ARIA grid on a div with no real structure works for screen readers and data extraction, even if it violates best practices. The detector reads what's there, not what the spec says should be there.

A single detection method. Most real-world pages use a mix. A sidebar navigation might be a nav list. The main content might be a CSS grid. A data table inside a modal might be a real <table>. All four co-exist on one page; the detector finds them all.

Code example

Here's a real <table>:

<table>
  <thead>
    <tr><th>Product</th><th>Price</th></tr>
  </thead>
  <tbody>
    <tr><td>Widget A</td><td>$9.99</td></tr>
    <tr><td>Widget B</td><td>$12.99</td></tr>
  </tbody>
</table>

The same data as a CSS grid:

<div class="price-table" style="display: grid; grid-template-columns: 1fr 1fr;">
  <div style="font-weight: bold;">Product</div>
  <div style="font-weight: bold;">Price</div>
  <div>Widget A</div>
  <div>$9.99</div>
  <div>Widget B</div>
  <div>$12.99</div>
</div>

The same data as repeated cards (a list of products):

<div class="products">
  <div class="product-card">
    <h3>Widget A</h3>
    <span class="price">$9.99</span>
  </div>
  <div class="product-card">
    <h3>Widget B</h3>
    <span class="price">$12.99</span>
  </div>
</div>

A detector should recognise all three as tables with the same logical structure.

How Scalpel Tables shows it

Each detected table appears as a card in the popup. At the top right of the card, a kind badge shows the detection method: <table>, ARIA, grid, or list. The same detection confidence score appears next to it; a card detected as a repeated-card list might score 65% confident, while a real table with headers scores 98%.

Hover over the badge to see why that detection was chosen. The preview below shows the extracted grid, so you can verify the detector got it right before you export.

Sources