This article was originally published on Smashing Magazine.
Tables are a design pattern for displaying large amounts of data in rows and columns, making them efficient for doing comparative analysis on categorical objects. Tables have been used for this purpose as early as the 2nd century and when the world started to go digital, tables came along with us.
It was inevitable that the web would support the display of data in a tabular format. HTML tables present tabular data in a semantic and structurally appropriate manner. However, the default styles on HTML tables are not exactly the most aesthetically pleasing thing you’ve ever seen. Depending on the desired visual design, some effort was required on the CSS front to prettify those tables. A decade ago, Top 10 CSS Table Designs was published on Smashing Magazine, and still continues to get a lot of traffic to this day.
The web has evolved a lot over the past decade, and it’s more convenient than ever to make your site or application adapt to the viewport it is viewed in. That being said, we have to continue to make considered design choices that do not compromise on accessibility. Since tables don’t seem to be falling out of favour any time soon, let’s see how we can do tables on the web in 2019.
Option 1: Do nothing
We’re going to start off with a low-effort scenario, if your data fits in a table with only a few columns and lots of rows, such a table is pretty much mobile-ready to begin with.
The issue you’d have is probably having too much room on wider screens, so it might be advisable to “cap” the maximum width of the table with a
max-width while letting it shrink as necessary on a narrow screen.
This sort of a pattern works best if your data itself isn’t lines and lines of text. If they are numeric, or short phrases, you can probably get away with not doing much.
Option 2: Style the scroll
David Bushell wrote up his technique for responsive tables back in 2012, which involved invoking overflow and allowing users to scroll to see more data. One could argue this isn’t exactly a responsive solution, but technically, the container is responding to the width of the viewport.
Let’s look at the “basic” overflow first. Imagine me using air-quotes around basic, because the styling for the scrolling shadows is anything but. But basic in this instance refers to the fact that the table does not transform in any way.
This technique for doing scrolling shadows comes from Roma Komarov and Lea Verou riffing off each other’s ideas to create magic. It hinges on using multiple gradients (linear and radial) as background images on the containing element, and using
background-attachment: local to position the background relative to its contents.
What’s nice about this technique is that for browsers that don’t support scrolling shadows, you can still scroll the table as per normal. It doesn’t break the layout at all.
Another scrolling option would be to flip the table headers from a row configuration to a column configuration, while applying a horizontal scroll onto the
<tbody> element’s contents. This technique uses flexbox behaviour to transform the table’s rows into columns.
display: flex to the table, it makes the
<tbody> both flex children, which are by default laid out next to each other on the same flex line.
We also make the
<tbody> element a flex container, thus making all the
<tr> elements within it flex children laid out in a single flex line as well. Lastly, every table cell has to have their display set to
block instead of the default
The advantage of this technique is that the headers are always in view, like a fixed header effect, so users don’t lose context as they scroll through the columns of data. One thing to take note of is that this technique results in a discrepancy of the visual and source order, and this makes things slightly unintuitive.
As mentioned earlier, layout options that involving morphing the table by modifying
display values sometimes have negative implications for accessibility, which require some minor DOM manipulation to rectify.
If you’re working with a relatively simpler dataset, perhaps you would like to write your own functions for some of these features.
Rows To Blocks, With Accessibility Fix
As far as I know of, this technique came about from the CSS Tricks article, Responsive Data Tables, by Chris Coyier back in 2011. It has since been adapted and expanded upon by many others.
The gist of this technique is to make use of a media query to switch the display property of the table element and its children to
block on a narrow viewport.
On a narrow screen, the table headers are visually hidden, but still exist in the accessibility tree. By applying data attributes to the table cells, we can then display labels for the data via CSS, while keeping the content of the label in the HTML. Please refer to the CodePen below for how the mark-up and styles look like:
The original method applies a width on the pseudo-element displaying the label text, but that means you’d need to know the amount of space your label needed to begin with. The above example uses a slightly different approach, whereby the label and data are each on opposite sides of their containing block.
We can achieve such an effect via auto-margins in a flex formatting context. If we set the display property for each
<td> element to flex, because pseudo-elements generate boxes as if they were immediate children of their originating element, they become flex children of the
After that, it’s a matter of setting
margin-right: auto on the pseudo-element to push the cell’s content to the far end edge of the cell.
Another approach doing the narrow viewport layout is using a combination of Grid and
display: contents. Please note that
display: contents in supporting browsers has issues with accessibility at the moment, and this method shouldn’t be used in production until those bugs are fixed.
But maybe you’re reading this after those bugs have been resolved, in that case, here’s an alternative layout option.
<tr> element is set to
display: grid, and each
<td> element is set to
display: contents. Only the immediate children of a grid container participates in a grid formatting context, in this case, the
display: contents is applied to the
<td>, it gets “replaced” by its contents, in this case, the pseudo-element and the
<span> within the
<td> become the grid children instead.
What I like about this approach is the ability to use
max-content to size the column of pseudo-elements, ensuring that the column will always be the width of the longest label, without us having to manually assign a width value for it.
In future, when the sizing values of
fit-content (covered by the CSS Intrinsic & Extrinsic Sizing Module Level 3) are supported as general
height values, we’ll have even more options for laying things out.
The downside to this approach is you do need that additional
<p> around the content in your table cell if it didn’t have one already, otherwise they’d be no way to apply styles to it.
This example shows a basic pagination implementation that was modified off this CodePen by Gjore Milevski to paginate on table rows instead of divs. It is an extension of the style the scroll example discussed in the previous section.
From a layout perspective, Flexbox comes in very handy for positioning the pagination elements across various viewport sizes. Different project designs will have different requirements, but the versatility of Flexbox should allow you to cater for these differences accordingly.
In this case, the pagination is centred on the page and above the table. The controls for navigating backward and forward are placed on either side of the page indicators on wider screens, but all four appear above the page indicators on narrow screens.
We can do this by using the
order property. Visual reordering of content should always be done with consideration because this property does not change the source order — only how it appears on screens.
It would be useful to have some sort of indicator of which column is currently being sorted and in what order. We can do that with the addition of CSS classes which can then be styled however you want. In this case, the indicator symbols are pseudo-elements that are toggled when the target header is clicked.
This example is a basic filtering functionality that iterates through all the textual content of each table cell and applies a CSS class to hide all rows that do not match the search input field.
Such an implementation is relatively naive, and if your use case calls for it, it might make sense to search per column instead. In that scenario, it might be a good idea to have each input field as part of the table in their respective columns.
Let a library handle it
The column toggle pattern is one whereby non-essential columns are hidden on smaller screens. Normally I’m not a fan of hiding content simply because the viewport is narrow, but this approach by Maggie Costello Wachs of Filament Group resolves that qualm of mine by providing a drop-down menu which allows users to toggle the hidden columns back into view.
The above article was published back in 2011, but Filament Group have since developed a full suite of responsive table plugins known as Tablesaw, which includes features like sorting, row selection, internationalisation and so on.
The column toggle feature in TableSaw also no longer depends on jQuery, unlike the examples from the original article. Tablesaw is one of the only libraries I could find that does not have a dependency on jQuery at the moment.
There are a myriad of table design patterns out there, and which approach you pick depends heavily on the type of data you have and the target audience for that data. At the end of the day, tables are a method for the organisation and presentation of data. It is important to figure out which information matters most to your users and decide on an approach that best serves their needs.
- CSS only Responsive Tables, David Bushnell
- Accessible, Simple, Responsive Tables, Davide Rizzo, CSS-Tricks
- Responsive table layout, Matt Smith
- Responsive design patterns: tables, Brad Frost
- Hey, It’s Still OK to Use Tables, Adrian Roselli
- Tables, CSS Display Properties, and ARIA, Adrian Roselli
- Data Tables, Heydon Pickering