The pseudo-semantic table

Flexbox and CSS Grid invited a naive expectation: that properties like align-self and justify-content would apply to block and inline elements generally — tables, lists, fixed elements — with the same syntax and outcomes regardless of the underlying reference boxes. They don't. But a hybrid can be built by stripping an HTML table of its layout behavior and reassembling it with CSS Grid: markup that reads like a table but lays out like a grid. Variants of the technique have already been used to make tables responsive.

Straight tables have real advantages, and this is not an argument against them. They give a clean structure that browsers understand and translate for assistive technology, and they offer a <caption> element. CSS Grid, meanwhile, remains necessary where intrinsic responsiveness, source order independence and positioning matter. HTML tables should still display actual tabular data. The question is narrower: whether the table's presentational flavor can be reused as markup.

Why table markup is worth keeping

Technically <table> is semantic rather than presentational like a <div>; browsers and authors share a common meaning for it. Call that meaning pseudo-semantic. Reading <th> signals a heading followed by data:

<div class="product-title">Wall Calendar</div>

Headings and <strong> carry title semantics but not the relationship between <th> and <td>, and <dl> terms are better reserved for textual structures such as glossaries than for layouts.

A table used purely as a pseudo-semantic markup skin raises a fair objection:

/* 🧟 */
table {
  display: grid;
}

Swap test: divs versus cells

Compare a generic-div version:

<article class="card">
  <div class="card-number">
    <strong>CARD NUMBER</strong>
    <span>1234 5678 9123 4567</span>
  </div>
  <div class="card-bank">
    <strong>CROWN <br>ATLANTIC <br>BANK</strong>
  </div>
  <div class="card-name">
    <strong>CARD HOLDER</strong>
    <span>JANE DOE</span>
  </div>
  <!-- more -->
</article>

with the table-based version:

<table class="card">
  <tr class="card-number">
    <th>CARD NUMBER</th>
    <td>1234 5678 9123 4567</td>
  </tr>
  <tr class="card-bank">
    <th>CROWN <br>ATLANTIC <br>BANK</th>
  </tr>
  <tr class="card-name">
    <th>CARD HOLDER</th>
    <td>JANE DOE</td>
  </tr>
  <!-- more -->
</table>

The corresponding CSS for the first, along with its result:

.card {
  display: grid;
  grid-template-columns: auto auto 1fr;
  gap: 2lh;
}

and the revised style for the table markup:

.card {
  display: grid;
  grid-template-columns: auto auto 1fr;
  gap: 2lh;

  /* Soul stripping */
  tbody { 
    /* Bypass tbody in the layout */
    display: contents; 
  }
  tr, tr > * { 
    /* Avoid table-row and table-cell styles */
    display: block; 
  }
}

The latter is easier to scan than a handful of generic divs, while still being laid out with CSS Grid. No browser has rejected the hybrid so far, and VoiceOver and keyboard navigation behave as they would with a real table.

We're encouraged to keep content and presentation separate, and this does exactly that without affecting the table's semantics, such as its role.

Perhaps it should affect them — that is the understandable discomfort. The construction looks gross and runs against the grain. But CSS is capable of bending plenty of other HTML elements the same way.