Menu

How to create responsive tables for your shop with CSS only

Resposive table with CSS only

There are a lot of methods out there to create responsive tables. I needed to create one for one of my clients and list the table on one of the website blog pages.

So to solve this problem I’ve used in the past a combination of HTML, CSS and JavaScript/jQuery to make the table responsive. Unfortunately the combination was not good for page speed because of the JavaScript code. If there is a simple CSS solution I recommend using it.

Usually I prefer to use as less as possible JavaScript code when I develop an ecommerce website because speed is one of the decisive factors for the client to make a sale or not.

Please note that I’ll not be writing pure CSS code and my code samples will be written directly in SASS. Shopify uses SASS and compiles to CSS.

For those that do not know what SASS is, please check this website.

Let’s start by creating the table code. We’ll create a table that will have 5 rows and 3 columns.

<table>
    <!-- Table head -->
    <thead>
        <tr>
            <th>Title</th>
            <th>Feature A</th>
            <th>Feature B</th>
        </tr>
    </thead>
    <!-- End Table head -->

    <!-- Table body -->
    <tbody>
        <tr>
            <td>Product name 1</td>
            <td>Product feature A</td>
            <td>Product feature B</td>
        </tr>
        <tr>
            <td>Product name 2</td>
            <td>Product feature A</td>
            <td>Product feature B</td>
        </tr>
        <tr>
            <td>Product name 3</td>
            <td>Product feature A</td>
            <td>Product feature B</td>
        </tr>
        <tr>
            <td>Product name 4</td>
            <td>Product feature A</td>
            <td>Product feature B</td>
        </tr>
        <tr>
            <td>Product name 5</td>
            <td>Product feature A</td>
            <td>Product feature B</td>
        </tr>
    </tbody>
    <!-- End Table body -->
</table>

You can either copy the code above and replace the text inside the cells with your own or you can create it yourself by writing the code from scratch.

Some of you may be pro developers and what you see in the code may be very easy for you, but some of my readers are shop DIY(Do It Yourself) owners and some beginner developers.

Because of that, I’ll explain the code above:

<table></table> – defines the table.

<thead></thead> – defines the table header.

<tbody></tbody> – defines the table body.

<tr></tr> – defines a table row.

<th></th> – defines a table header cell. This type of cells contains header information.

<td></td> – defines a table data cell. This type of cells contains data information.

<!– End Table body — > – these are comments inside the HTML code. Comments will help you understand better the code that you write.

Our table from above when rendered by the browser will look like this:

COLUMN 1COLUMN 2COLUMN 3
Product name 1Product feature AProduct feature B
Product name 2Product feature AProduct feature B
Product name 3Product feature AProduct feature B
Product name 4Product feature AProduct feature B
Product name 5Product feature AProduct feature B

In order to make this table responsive on small devices we will make the first column(COLUMN 1) fixed or sticky, meaning that when you swipe left the other 2 columns(COLUMN 2 and COLUMN 3) will become mobile.

To achieve a scroll effect we need to add a parent container to the table.

We will give to the parent container a simple name like “container”. The element will look like this:

<div class="container">...</div>

We will replace the ellipsis(…) with the table code that we’ve written above and the final code will look like this:

<!-- Table parent container -->
<div class="container">
  <table>
    <!-- Table head -->
    <thead>
      <tr>
        <th>Title</th>
        <th>Feature A</th>
        <th>Feature B</th>
      </tr>
    </thead>
    <!-- End Table head -->

    <!-- Table body -->
    <tbody>
      <tr>
        <td>Product name 1</td>
        <td>Product feature A</td>
        <td>Product feature B</td>
      </tr>
      <tr>
        <td>Product name 2</td>
        <td>Product feature A</td>
        <td>Product feature B</td>
      </tr>
      <tr>
        <td>Product name 3</td>
        <td>Product feature A</td>
        <td>Product feature B</td>
      </tr>
      <tr>
        <td>Product name 4</td>
        <td>Product feature A</td>
        <td>Product feature B</td>
      </tr>
      <tr>
        <td>Product name 5</td>
        <td>Product feature A</td>
        <td>Product feature B</td>
      </tr>
    </tbody>
    <!-- End Table body -->
  </table>
</div>
<!-- End Parent container -->

Great. This is all the markup that we need for our table. Please check again that your markup is correct and let’s proceed with the SASS/CSS code.

Let’s style the table:

$border-color: #ddd;
$first-column-background-color: #eee;

.container {
  table {
    border-collapse: collapse;
  }

  th {
    padding: 1rem 3rem;
    border: 1px solid $border-color;
    white-space: nowrap;
  }

  td {
    padding: 1rem;
    border: 1px solid $border-color;
    white-space: nowrap;
  }
}

To achieve the desired effect we use a “new” position: sticky. Up until now maybe you knew four CSS Positions: static, relative, absolute and fixed. If you didn’t know about “sticky” please add it to your toolbox because it is a great way to achieve great stuff.

Position “sticky” has good support worldwide, more than 90% according to Can I Use . Of course as usual it will not work on IE11 and below. If you need support for IE I recommend providing a fallback.

First we need to add a property for the container to scroll when the content is bigger than the container, overflow-x: scroll. This means that when the content will go outside the parent container the browser will force it to scroll in our case on horizontal.

.container {
  overflow-x: scroll;
  border: 1px solid $border-color;
}

We’ll also add a border around the container. The border is a fix for when we’ll remove the table borders entirely in the bellow code for the first column.

OK. We’ve styled the parent container, now we need to target the first table column. We do that by writing the following code:

// Mobiles(Portrait)
@media screen and (max-width: 30rem) {
  .container {
    th,
    td {
      &:first-child {
        background: $first-column-background-color;
        position: sticky;
        left: 0;
        z-index: 1;
        border: 0;
        outline: 1px solid $border-color;
      }
    }
  }
}

Please note that I’m targeting small mobile devices like smartphones in portrait mode for my above example.

Now we target the first column by selecting every first “th” and “td” elements and we add the following properties:

background: $first-column-background-color; – This is defined as a SASS variable above and it will be used to give a different background color to the first column to differentiate COLUMN 1 from the other 2 columns(COLUMN 2 & COLUMN 3).

position: sticky; – This tells the browser to set the first column fixed, in our case to left but to achieve that we also need to tell the browser that we want to make the column fixed to the left side of the parent container by setting left: 0;

For a sticky position to work you must specify at least one of the directions: top, right, bottom or left.

z-index: 1; – Will make sure that the first column will always be above the other 2 columns in the table. Once you start scrolling(swipe) the second and the third columns, they will go below the first column. The first column will always remain on top.

border: 0;  – This will set all borders of the first column to zero. Why? Because we use “border- collapse: collapse;” the border will not be visible when using position sticky.

outline: 1px solid $border-color; – To fix the borders issue we use outline to emulate the borders.

Let’s remove  the double borders now. Once you complete writing the code above, you’ll notice double borders on the table top(except the first column), right and bottom. To remove those borders add the following code:

.container {
  th,
  td {
    &:last-of-type {
      border-right: 0;
    }
  }

  th {
    border-top: 0;
  }

  tr {
    &:last-of-type {
      td {
        border-bottom: 0;
      }
    }
  }
}

Here’s the full code:

$border-color: #ddd;
$first-column-background-color: #eee;

.container {
  table {
    border-collapse: collapse;
  }

  th {
    padding: 1rem 3rem;
    border: 1px solid $border-color;
    white-space: nowrap;
  }

  td {
    padding: 1rem;
    border: 1px solid $border-color;
    white-space: nowrap;
  }
}

// Mobiles(Portrait)
@media screen and (max-width: 30rem) {
  .container {
    overflow: scroll;
    border-left: 1px solid $border-color;
    border-right: 1px solid $border-color;

    th,
    td {

      &:first-child {
        background: $first-column-background-color;
        position: sticky;
        left: 0;
        z-index: 1;
        border: 0;
        outline: 1px solid $border-color;
      }
     
      &:last-of-type {
        border-right: 0;
      }
    }
    
    th {
      border-top: 0;
    }

    tr {
      &:last-of-type {
        td {
          border-bottom: 0;
        }
      }
    }
  }
}

This is how your table should look and work:

To view the effect please resize the browser or access the page via a mobile device.

COLUMN 1COLUMN 2COLUMN 3
Product name 1Product feature AProduct feature B
Product name 2Product feature AProduct feature B
Product name 3Product feature AProduct feature B
Product name 4Product feature AProduct feature B
Product name 5Product feature AProduct feature B

Click or tap inside the COLUMN 2 and swipe or scroll to right or left.

Please note that this is a solution that I’ve found while trying to solve a problem. In my case this was perfect because it did what I needed with the least amount of code.

If you can help improve the technique or you found a better one please let me know by using the contact page. I’ll be more than happy to feature your solution on the blog.