(Read the revised version of this post here)

We all know what a pain it can be to get tables to act the way we want them especially on different devices.

Not to long a go a post showed up on the internet explaining a different approach to responsive tables using divs. After reading the post and looking at the example it got me to think there must be an easier way to this. So I started fumbling around with some HTML and CSS and this is what I came up with. It’s by no means the perfect solution, but its another step closer to an acceptable way displaying tables.

Some notable statistics:

  • Fully responsive table layout
  • No use of Javascript
  • Use of data attributes for labeling on mobile devices
  • +/- 50 lines of CSS
  • Thats all it takes!
Note: This approach uses div's instead of the regular table element. This means its not semanticly correct.

What we’ll get

Obviously this is a very simple version of a price table, you can customize this any way you want!

Setting up the basic HTML

First things first, lets start with the HTML. We are going to split the the vertical columns in seperate elements on mobile. Don’t worry if you don’t follow, this will make more sense later.

Setting up the table with the vertical columns inside it.

<div class="rs-table">
    <div class="rs-col">
    	column 1
    </div>
    <div class="rs-col">
    	column 2
    </div>
    <div class="rs-col">
    	column 3
    </div>
</div>

Now we’ll insert the cells within the vertical rows, which we will later horizontally align using css.

<div class="rs-col">
    <div class="rs-cell">
    	Column 1 cel 1
    </div>
    <div class="rs-cell">
    	Column 1 cel 2
    </div>
    <div class="rs-cell">
    	Column 1 cel 3
    </div>
</div>

Do this for all your columns.

By now we should have our basic HTML set-up. We have a table wrapper with the class .rs-table, we have vertically divided the columns using the class .rs-col and we have seperated the vertical elements using the class .rs-cell.

Lets start adding some CSS!

Adding the first CSS styles

To make sure our table displays itself correctly we need to add some styles which will determine the behaviour of our columns and rows.

Adding some simple styling to the table wrapper, I am not going to explain every basic line of CSS used here, I suggest following some online HTML/CSS courses if these things are still somewhat unclear to you.

.rs-table {
    display: block;
    clear: both;
    margin: 50px 0;
    font-size: 14px;
    text-align: center;
}

Now lets get to the good part: Creating the table layout!

First off we start with the horizontal columns.

.rs-col {
    display: block;
    float: left;
    width: 20%;
    max-width: 350px;
    min-width: 180px;
}

Nothing really new here. Adjust the max-width to your own liking, or set it to just width if you want a fixed width for all columns.

Now lets style the cells within the columns.

.rs-cell {
    display: block;
    min-height: 30px;
    width: 100%;
    padding: 20px 25px;
    box-sizing: border-box;
    border-right: 1px solid #ddd;
    border-bottom: 1px solid #ddd;
}

We have added a min-height here to keep the height of the rows identical, this has to stay the same for all cells in order for the table rows to stay horizontally aligned. I have used box-sizing to keep the dimensions of the cells while playing with the padding (more on this here);

By now our table should start to take shape but it is still far from finished. Next we will add some styling determing the labels by which the columns and rows will be compared.

Basicly what we want is the first horizontal row and the first vertical column to have a font weigh higher than the rest. We can achive this by adding a class to the first div with the class .rs-col and adding a class to every first rs-cell of every column.

The class .rs-legend-h will be used to mark the horizontal legend and the class .rs-legend-v will be used to mark the vertical legend.

<div class="rs-col rs-legend-v">
    <div class="rs-cell rs-legend-h">
        Column 1 cel 1
    </div>
    <div class="rs-cell">
        Column 1 cel 2
    </div>
    <div class="rs-cell">
        Column 1 cel 3
    </div>
</div>
<div class="rs-col">
    <div class="rs-cell rs-legend-h">
        Column 2 cel 1
    </div>
    <div class="rs-cell">
        Column 2 cel 2
    </div>
    <div class="rs-cell">
        Column 2 cel 3
    </div>
</div>
<div class="rs-col">
    <div class="rs-cell rs-legend-h">
        Column 3 cel 1
    </div>
    <div class="rs-cell">
        Column 3 cel 2
    </div>
    <div class="rs-cell">
        Column 3 cel 3
    </div>
</div>

Adding content to our table

I made some changes to the table making it more realistic and a better representation of what we are trying to accomplish. Besides inserting realistic content I added call to actions buttons to each column, I added zebra stripes for clarity and I have changed the font size and weight for the prices (note: I needed to add a class to the entire row containg the cells for the prices because they needed to be higher than the other cells, for horizontal consistancy).

See the Pen Simple responsive table by Wouter (@wouterhillen) on CodePen.

Now its starting to look more like a real price table!

But what about mobile? As you can probably see it does not really work well on mobile resolutions. So lets get to the good part!

Making the table work well on mobile devices

To get the table to display itself correctly on mobile devices we need to add a couple of rules to our css.

First of I’m setting my cut-off breakpoint at 768 pixels but you can change this to whatever you like or what fits your websites needs.

Lets add the breakpoint media query to our css

@media screen and (max-width: 768px) {
    // Mobile stuff here
}

First thing we are going to adjust is our column width, seeing as we are not going to be able to fit more than one column in the view horizontally we should give them 100% width.

@media screen and (max-width: 768px) {

    .rs-col {
        width: 100%;
        max-width: 100%;
        float: none;
        margin: 10px 0;
        border: 1px solid #ddd;
    }

    .rs-cell {
        border: 0;
    }

}

I also usually remove the float to avoid bugs later on and add some margin top and bottom to seperate the elements from each other vertically on mobile. I also removed the borders from the cells and added one border around the entire element.

These changes alone already make a huge difference as you can see below.

Now we are left with two problems:

  1. We still have a useless legend floating around above the price elements
  2. Our individual price elements do not have any labels identifying the values inside the cells

To fix the first problem we can simply hide the div with the class .rs-legend-v. To tackle the second problem we are going to add a cool css trick.

Que the data attribute !

Using data attributes with CSS

Using the HTML data attribute in combination with the CSS pseudo element :before or :after we can transport data within our HTML file using CSS. This may sound difficult but its pretty straight forward.

First we are going to add data attributes to our HTML, these data attributes will describe the row the individual cell belongs to. We will be calling our data attribute data-legend. To keep things simple and consistent you should probably give these the same values as your table legend.

<div class="rs-col">
    <div class="rs-cell rs-legend-h">
      Basic
    </div>
    <div class="rs-cell">
      <span class="price">29<small>/mo</small></span>
    </div>
    <div class="rs-cell" data-legend="Accounts">
      15
    </div>
    <div class="rs-cell" data-legend="Space">
      100
    </div>
    <div class="rs-cell" data-legend="Domain name">
      1 domain name
    </div>
    <div class="rs-cell" data-legend="Bandwidth">
      500 GB Bandwidth
    </div>
    <div class="rs-cell">
      <a class="button">Buy Basic</a>
    </div>
</div>

Obviously you could fill these data attributes automaticly using the Javascript grabbing the values from your legend column, this would make your table a bit more dynamic and faster to implement but for the sake of keeping this tutorial simple we will not delve deeper into this right now.

Now when we are viewing our tables on a device with a higher resolution than our set breakpoint width nothing will need to happen, but when we view our table on a mobile device we need to show the legend values in the same cell as the specific values.

This will be achived using the :before pseudo element and its ability to use data attributes in its content value.

We will add this to our CSS within the breakpoint media query

.rs-cell:before {
    content: attr(data-legend);
    display: block;
    font-size: 14px;
    font-weight: bold;
    padding: 2px 0;
}

This will create a block element containing the data from the data attribute added to our .rs-cell element within the individual cell.

Because we placed this CSS within our media query it will not do anything on desktop resolutions. Another great advantage to this method is your source code will not get cluttered with all these labels because pseudo elements do not show up in your source code.

And there you have it! Our responsive table is pretty much finished. Using no Javascript and minimal CSS we’ve created this highly re-usable template for displaying prices and packages on desktop and mobile devices. You can expand on this anyway you like for example by using Javascript to create the labels dynamicly or using SCSS to reduce the lines of CSS needed even more.

I hope everything in this tutorial was clear and understandable, if you have any questions or suggestions just leave a comment below and I will try my best to answer.

⇠ Go back to all posts