Here’s a typical data table with column headers at the top and rows below.

On mobile devices the orientation changes from horizontal to vertical, and each cell is prepended with its respective table header as a label.

I was going for a really clean look where labels and cells would line up with the previous row, but I also wanted it to be fluid, so I could just plug it into any site without having to adjust widths in the future.

There are a few things at play here:

A Class: The JavaScript is scoped by .js-table-data which needs to be included on <table>. This will keep it from messing with any other tables you may have, or just remove that class from the js if you want it to apply to every table on your site.

Some CSS: I used a hack I previously wrote about to add a pseudo element displayed as a table-cell, which tricks the remaining part of the element to behave like a separate table-cell, as long as the original element is displayed as a table-row. It’s confusing, but the code and live example should help.

A Media Query: I know there’s a lot of dogma about mobile-first CSS … but I used a max-width media query to keep things simple. The alternative is overwriting a lot of values for the desktop which seems unnecessary.

Dash of JavaScript: The js adds data attributes, which contain the header text, to each table-cell. I used jQuery methods, so don’t forget to include the library. Alternatively, you can pull this off with CSS alone, which I discuss at the end of this post. However, the js makes it very easy to add this to any website, with multiple tables, and still works [within reason] if a content editor adds or pastes a table.

Different Headers for Mobile

If you need to modify a header on mobile devices for whatever reason (e.g. “Description” could be shortened to “Desc”), you can manually add data-label to a specific <th>, and the JavaScript will grab that instead of the inner text.

<th data-label='Desc'>Description</th>

CSS-Only Solution

You may not need to include the JavaScript. If your table rows are generated within a loop, AND you don’t have a lot of different tables with different headers, then you can manually add the data attributes to each <td> within the loop, and omit the JavaScript.

<td data-label='Name'>Name</td>

Potential issues for CSS-only solution:

Not plug-n-play on every site and new projects.

If the table rows aren’t in a loop, then you’ll have to add the data attrs to each cell in every row.