Responsive and SEO Friendly Data Tables
First appeared on mobifreaks.com · Updated
This was one of the most popular posts on Mobifreaks. The idea was a data table that reflowed into stacked cards on phones, with each value labelled. The responsive part still holds up. The "SEO friendly" part, building the table out of divs, does not, so I have rewritten that half.
What I did in 2011
We had all moved away from table-based layouts, and I believed tables were bad for search engines. So I rebuilt the table with divs and gave them the browser's own table display values:
.table { display: table; }
.table-head { display: table-header-group; }
.row { display: table-row; }
.column { display: table-cell; padding: 10px 20px; border-bottom: 1px solid #eeeeee; }
<div class="table">
<div class="table-head">
<div class="column">Employee No</div>
<div class="column">Real Name</div>
<div class="column">Profession</div>
</div>
<div class="row">
<div class="column" data-label="Employee No">2311</div>
<div class="column" data-label="Real Name">Ipsum dolor</div>
<div class="column" data-label="Profession">Web designer</div>
</div>
</div>
Then, under 640px, a media query turned every row into a block, hid the header and printed each cell's data-label in front of its value with content: attr(data-label). That trick is the part worth keeping.
What I got wrong
Tables were never bad for SEO. They were bad for layout. For tabular data, a real <table> is exactly right: search engines understand it, and screen readers can announce the column header for every cell, which a pile of divs can't do.
The same idea today
Keep the real table, add the labels, and only change the display on small screens.
<table class="data">
<caption>Employees</caption>
<thead>
<tr><th scope="col">Employee No</th><th scope="col">Real Name</th><th scope="col">Profession</th></tr>
</thead>
<tbody>
<tr>
<td data-label="Employee No">2311</td>
<td data-label="Real Name">Ipsum dolor</td>
<td data-label="Profession">Web designer</td>
</tr>
</tbody>
</table>
.data { border-collapse: collapse; width: 100%; }
.data th,
.data td { padding: 0.625rem 1.25rem; border-bottom: 1px solid #eeeeee; text-align: left; }
@media (width <= 40rem) {
.data thead {
/* visually hidden, still read by screen readers */
position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
}
.data,
.data tbody,
.data tr,
.data td { display: block; }
.data tr { margin-block: 1.25rem; border: 1px solid #eeeeee; }
.data td::before {
content: attr(data-label);
display: block;
font-weight: bold;
font-size: 0.75rem;
}
}
@media (width <= 40rem)is the newer range syntax formax-width. All current browsers support it.- The header is hidden visually rather than with
display: none, the same trick I used in 2011 withtop: -1000em, just tidier. - One catch: some browsers drop a table's meaning for screen readers once you change its
display. If you need to be sure, addrole="table",role="row",role="columnheader"androle="cell"to the elements. - If the columns are mostly short, a simpler option is to let the table scroll sideways inside a wrapper with
overflow-x: auto.
A follow-up post, Responsive Data Tables with jQuery, did the 2011 conversion automatically.