Your Current Language Is:

Responsive CSS Table Design In Practice & Execution

We recently launched a new DNS Product feature comparison table to help users more easily compare our DNS products and what they offer. I’m eager to make all of this content accessible for people on smaller screens (particularly people on mobile devices), but it’s very hard to make tables look as good as they do on wider screens.

Here’s what we did and how we did it.

nth-of-type:before trick

I came across this post on CSS Tricks about how to build responsive tables by adding content to each table cell in a media query for smaller screen sizes. Basically, you eliminate the main table header row and add that content back in to each individual cell so that it’s easily scanned on a smaller screen. This technique uses nth-of-type to count through tds and add content, which works great on simple tables.

Unfortunately, I have a much more complex one:

If I didn’t have any table cells that spanned multiple columns, I could just use CSS Trick’s :before trick for each column and I’d be all set. But because we have some column spans, the count of table cells gets wonky.

For example: in the first row of data below the header, the same information spans DynECT Managed DNS Lite 10, 25, and 50 cells. Therefore the fourth td in the row is the Enterprise table cell. In the next row, though, there are separate tds for Lite 10, 25, and 50, so the Enterprise cell data is in the sixth td. This means I can’t just find the fourth or sixth td to add the Enterprise label, so I needed to get a little creative!

nth-of-type and colspan*=x

First, set everything in the table to display block and automatically decide its width.

table, th, td, tr { display: block; text-align: left; width: auto; }

Next, hide the header row.

thead tr { position: absolute; top: -9999px; left: -9999px; }

Let’s set up our table cell headers with some styling. We’re using :before to add in the content, so I want to display that as a block-level element and bold our label text.

td:before { display: block; font-weight: bold; }

Time to add the labels! Here’s where we have to do some fancy counting. I primarily use two kinds of selectors:

td:nth-of-type(x) which finds the nth td in the row

td[colspan*="x"] which finds all tds that have a particular column span

Let’s start easy. The first cell is most likely DynDNS Pro:

td:nth-of-type(1):before { content: "DynDNS Pro: "; }

The second td is most likely Standard DNS:

td:nth-of-type(2):before { content: "Dyn Standard DNS: "; }

But sometimes, we combine DynDNS Pro and Standard DNS in the same cell. In this case, we can target it knowing it spans two columns:

Moving along, let’s figure out how to best target DynECT Managed DNS Lite. It’s often the third td, so we should catch that. It may also be the fourth and fifth tds in the case where we split out the tier information, so let’s grab that.

We also have a case where it spans the third, fourth and fifth tds using colspan=3, so we target that too.

And that takes care of it! We used the power of nth-child, colspan, and cascading power to add content in each td that clearly labels what original column header it applied to. You can see what the end result looks like on the right!

Lara Swanson is the lead front-end web developer for Dyn. You can follow her thoughts on coding semantically, nitpicking page load time, and the importance of baking for coworkers on Twitter.