A little while back I wrote an article about responsive table design using divs to recreate the table layout (read the previous article here). After posting the article on my blog some people pointed out to me that using divs was not really semantically correct, which they were right about. So in this article I am going to create a responsive price table using the actual table element and not just divs.

The one thing that is going to be different with this article compared to others is that in this article we are going to create a pricing table with vertically aligned columns from left to right, instead of the regular horizontally aligned rows.

The HTML

<table class="rs-table">
  <tr class="rs-legend" data-label="table legend">
    <td data-label="Empty cell"></th>
    <td data-label="Legend">Accounts</td>
    <td data-label="Legend">Price</td>
    <td data-label="Legend">Space</td>
    <td data-label="Empty cell"></td>
  </tr>
  <tr>
    <th scope="col" data-label="Title">Basic</th>
    <td data-label="Accounts">3</td>
    <td class="price" data-label="Price">&euro;25 <span>/mo</span></td>
    <td data-label="Space">20gb</td>
    <td><button class="button">Order now</button></td>
  </tr>
  <tr>
    <th scope="col" data-label="Title">Advanced</th>
    <td data-label="Accounts">10</td>
    <td class="price" data-label="Price">&euro;45 <span>/mo</span></td>
    <td data-label="Space">100gb</td>
    <td><button class="button">Order now</button></td>
  </tr>
  <tr>
    <th scope="col" data-label="Title">Pro</th>
    <td data-label="Accounts">20</td>
    <td class="price" data-label="Price">&euro;65 <span>/mo</span></td>
    <td data-label="Space">500gb</td>
    <td><button class="button">Order now</button></td>
  </tr>
</table>

I am using data-attributes to name the table cells they make the table a little bit more accessible for screenreaders and other accessibility tools and we will be using them als labels later.

Note: I was using aria labels in my example before but as Aaron pointed out to me, these actually overwrite the accessible name/value of the element, so data-attributes would be a better fit here.

As you can see I’ve also added something called a scope attribute, these tell the browser if the cell is a head element for a row or a column, in our case all first cells are headings for our columns. Seeing as we switched the layout of our table rows (from left to right instead of top to bottom), it might help labeling these elements using the scope attribute. Beside these elements I’ve added some classes but these are purely for styling purposes (except for the rs-legend class but we’ll get into that a bit later).

The CSS (SASS)

This time I have chosen to use SASS but you could easily use regulary CSS to accomplish the same by replacing the variables with your desired widths.

// Variables
$tableWidth: 1000px;
$numberRows: 4;

.rs-table {
  width: 100%;
  max-width: $tableWidth;
  
  tr {
    width: 100% / $numberRows;
    display: block;
    float: left;
    overflow: hidden;
  }
  
  td, th {
    display: block;
  }
}

The CSS shown above turns the tr row element into a vertical column and gives each column an equal width relative to the current width of the parent table divided by the number of columns. In this case that would be 25% (4 columns).

Adding the style and the responsiveness

// Extra styling

@import 'https://fonts.googleapis.com/css?family=Open+Sans';

body {
  background: #fafafa;
  font-family: 'Open sans';
  -webkit-font-smoothing: antialiased;
}

body * {
  box-sizing: border-box;
}

.rs-table {  
  
  tr {    
    background: #fff;
    border-right: 1px solid #ddd;
    
    &:first-child {
      border-left: 1px solid #ddd;
    }
  }
  
  td, th {
    min-height: 100px;
    line-height: 42px;
    margin: 0;
    text-align: center;
    padding: 1.8em 1.2em;
    border-bottom: 1px solid #eaeaea;
    
    &.price {
      font-size: 1.2em;
      line-height: 28px;
      font-weight: bold;
      color: #f55567;
      
      span {
        font-size: 0.7em;
      }
    }
  }
    
  th {
    font-weight: bold;
    font-size: 1.2em;
    line-height: 1.2em;
  }
  
}

.button {
  color: #fff;
  border: 0;
  padding: 10px 12px;
  border-radius: 3px;
  background: #f55567;
  cursor: pointer;
  font-size: 1em;
  
  &:hover {
    opacity: 0.8;
  }
}

@media screen and (max-width: 768px) {
  
  .rs-table {    
    tr {
      width: 100%;
      max-width: none;
      display: block;
      margin: 0 0 5em 0;
      border: 1px solid #ddd;
      overflow: hidden;
    }
    
    .rs-legend {
      display: none;
    }
    
    td, th {
      width: 100%;
      padding: 2em 0;
      margin: 0;
      display: block;
      border: 0;
      border-bottom: 1px solid #eaeaea;
      
      &:last-child {
        border-bottom: 0;
      }
    }
      
    td:before {
      content: attr(data-label);
      display: block;
      font-weight: bold;
    }    
  }
  
}

In these lines of CSS we make every tr column 100% wide. And seperate them individually with a top and bottom margin. I removed the legend column because we are using the data-labels to display a label in each cell.

I also added some styling to make the whole thing look more like a real price table!

If you add all this up you should get something like this:

See the Pen Simple, semantic and responsive tables by Wouter (@wouterhillen) on CodePen.

(Scale your browserwindow to see it in action)

This would be a more semantic responsive table! Thanks for reading and I hope you learned something from my example, if you have any questions or comments please leave them below and I will try to get back to you as soon as possible.

⇠ Go back to all posts