Mobile Responsive Table with TailwindCSS
Tags: tailwindcss
Published: 2023-01-16 23:03:25

Recently I pair programmed with a Senior Software Engineer and part of the story we were working on was implementing the design for a list of users with their relevant data displayed. The design looked similar to this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p7jzethv67rb5a5icl57.png)

We also knew we wanted it to be mobile responsive even though we didn’t have any designs for that.

## Accessibility Concerns

We first tried building it using a [description list](https://www.w3schools.com/tags/tag_dl.asp#:~:text=The%20tag%20defines%20a,describes%20each%20term%2Fname) `dl` with `dt` which defines the terms/names and `dd` which describes each term/name. We actually got really close to the design using this, but then we used a screen reader to see what the experience would be like. It was at best cumbersome, and at worst unusable. The problem is that this is actually a list of tabular data; even if it doesn’t look like it based on the design and the lack of traditional headers. The way the screen reader goes through each definition is by reading the term then the description. This means that the information is split up so you can’t scroll through the list of emails, instead you have to go through all the elements of the description list to get to a specific email. When using `table` tags, the screen reader can read down each column of data which is a much better experience.

## Building the Table 

So we scrapped the description list and built it using the HTML `table` element and used `sr-only` to hide the headers from a visual user but including that important information for screen readers.

Once we had the table data we started styling each cell in `td`.

```HTML
  <table>
    <thead class="sr-only">
      <tr>
        <th>Name</th>
        <th>Email</th>
        <th>Company</th>
        <th>Status</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="px-4 py-3 text-gray-900 bg-gray-200 first:rounded-tl-lg first:rounded-bl-lg last:rounded-tr-lg last:rounded-br-lg">Frodo Baggins</td>
        <td class="px-4 py-3 text-gray-900 bg-gray-200 first:rounded-tl-lg first:rounded-bl-lg last:rounded-tr-lg last:rounded-br-lg">fbaggins@mail.com</td>
        <td class="px-4 py-3 text-gray-900 bg-gray-200 first:rounded-tl-lg first:rounded-bl-lg last:rounded-tr-lg last:rounded-br-lg">Fellowship of the Ring</td>
        <td class="px-4 py-3 text-gray-900 bg-gray-200 first:rounded-tl-lg first:rounded-bl-lg last:rounded-tr-lg last:rounded-br-lg">Active</td>
      </tr>
    </tbody>
  </table>
```
The idea is that we want to reuse the classes for each cell, so we use the `first` and `last` modifiers to target rounding the border just for the top and bottom left for the first column, and the top and bottom right for the last column.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yefjtoflfrlf9srfjlo1.png)

To add the tag-like styling to “Active“ we wrap it in a span:

```HTML
<span class="float-right rounded-md bg-green-600/50 px-4 py-px text-xs font-semibold uppercase text-green-900 antialiased">Active</span>
```
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o2we103w4sfoyxbz7yjz.png)

At this point we wanted to add more rows, but didn’t want to continue repeating reused classes. We were working in a Phoenix LiveView app so we created components. In the interest of focusing on the Tailwind here, I will pull the reused classes into the CSS file and use `@apply`.

CSS:

```CSS
@tailwind base;
@tailwind components;
@tailwind utilities;

.td-class {
  @apply px-4 py-3 text-gray-900 bg-gray-200 first:rounded-tl-lg first:rounded-bl-lg last:rounded-tr-lg last:rounded-br-lg
} 
```
HTML:
```HTML
<table class="">
  <thead class="sr-only">
    <tr>
      <th>Name</th>
      <th>Email</th>
      <th>Company</th>
      <th>Status</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td class="td-class">Frodo Baggins</td>
      <td class="td-class">fbaggins@mail.com</td>
      <td class="td-class">Fellowship of the Ring</td>
      <td class="td-class">
        <span class="float-right rounded-md bg-green-600/50 px-4 py-px text-xs font-semibold uppercase text-green-900 antialiased">Active</span>
      </td>
    </tr>
  </tbody>
</table>
```

Now we can add more rows easily and start styling the table. We need to add some spacing between each of the rows which we can do using Tailwind’s [Border spacing](https://tailwindcss.com/docs/border-spacing#setting-the-border-spacing) for tables. Let’s also add the default text size of small and wrap the table in a div so we can center it using flex.

```HTML
<div class="flex items-center justify-center">
  <table class="text-sm border-separate border-spacing-y-2">
    <thead class="sr-only">
      <tr>
        <th>Name</th>
        <th>Email</th>
        <th>Company</th>
        <th>Status</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="td-class">Frodo Baggins</td>
        <td class="td-class">fbaggins@mail.com</td>
        <td class="td-class">Fellowship of the Ring</td>
        <td class="td-class">
          <span class="float-right rounded-md bg-green-600/50 px-4 py-px text-xs font-semibold uppercase text-green-900 antialiased">Active</span>
        </td>
      </tr>

      <tr>
        <td class="td-class">Bilbo Baggins</td>
        <td class="td-class">bbaggins@mail.com</td>
        <td class="td-class">Thorin’s Fellowship</td>
        <td class="td-class">
          <span class="float-right rounded-md bg-green-600/50 px-4 py-px text-xs font-semibold uppercase text-green-900 antialiased">Active</span>
        </td>
      </tr>
    </tbody>
  </table>
</div>
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0vo3693iki4pi9ckb4t5.png)

We still need to add the tags for when a user’s status is “Pending” or “Suspended“, so let’s add a few examples for that.

```HTML
<div class="flex items-center justify-center">
  <table class="border-separate border-spacing-y-2 text-sm">
    <thead class="sr-only">
      <tr>
        <th>Name</th>
        <th>Email</th>
        <th>Company</th>
        <th>Status</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td class="td-class">Frodo Baggins</td>
        <td class="td-class">fbaggins@mail.com</td>
        <td class="td-class">Fellowship of the Ring</td>
        <td class="td-class">
          <span class="float-right rounded-md bg-green-600/50 px-4 py-px text-xs font-semibold uppercase text-green-900 antialiased">Active</span>
        </td>
      </tr>

      <tr>
        <td class="td-class">Peregrin Took</td>
        <td class="td-class">pippin@mail.com</td>
        <td class="td-class">Fellowship of the Ring</td>
        <td class="td-class">
          <span class="float-right rounded-md bg-green-600/50 px-4 py-px text-xs font-semibold uppercase text-green-900 antialiased">Active</span>
        </td>
      </tr>

      <tr>
        <td class="td-class">Bilbo Baggins</td>
        <td class="td-class">bbaggins@mail.com</td>
        <td class="td-class">Thorin’s Company</td>
        <td class="td-class">
          <span class="float-right rounded-md bg-yellow-600/50 px-4 py-px text-xs font-semibold uppercase text-yellow-900 antialiased">Pending</span>
        </td>
      </tr>
      <tr>
        <td class="td-class suspended-text">Boromir of Gondor</td>
        <td class="td-class suspended-text">boromir@mail.com</td>
        <td class="td-class suspended-text">Fellowship of the Ring</td>
        <td class="td-class">
          <span class="float-right rounded-md bg-red-600/50 px-4 py-px text-xs font-semibold uppercase text-red-100 antialiased">Suspended</span>
        </td>
      </tr>
    </tbody>
  </table>
</div>
```

```CSS
.suspended-text {
  @apply text-gray-500
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mbncf7c3wrfa62lnov5n.png)

For the row with a status of “Suspended“ the text color is a lighter shade of gray. Since the current color comes from the  CSS file as part of the `td-class`, we have to add another class so that when the CSS is compiled it will overwrite it with the suspended text color. CSS that is in the CSS file is compiled after the inline utility classes. The consequence of this is that if you try `td-class text-gray-500`, it will still render as `text-gray-900`. This is one reason why it’s highly recommended that you componetize your reused classes instead of using `@apply`.

Great! This looks very close to the design we were given to work with! But, if you resize it down to a mobile phone, it doesn’t look great and in fact some of the text is cut off.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ihhzst5bvw0m26vjmcyb.png)

Let's fix that! We’ll make it very simple and render each row in a more readable way.

## Making the Table Mobile Responsive

To prevent the text from being cut off at the front, we add a breakpoint of `sm:justify-center` and some margin to the containing div.

```HTML
<div class="flex items-center sm:justify-center ml-4 sm:ml-0">
```

We add `class="tr-class"` to each table row and make it a flex container with a column direction so that information inside each card stack on top of each other with some margin in between in each card.

```CSS
.tr-class {
  @apply flex flex-col mb-4
}
``` 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ks8gliq5o4hgd6r0oi4z.png)

It looks a little goofy because of the border rounding in our `td-class` that we implemented for the first iteration, we need to adjust the rounding for the mobile size.

```CSS
.td-class {
  @apply px-4 py-3 bg-gray-200 first:rounded-t-lg last:rounded-b-lg
} 
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h400assg74m239fbiklh.png)

This looks much better! But if you resize it to tablet or desktop size, it stays as those cards instead of expanding to long rows. We need to add a breakpoint of `sm:table-row` to the `tr-class`. This adds the [display `table-row`]( https://tailwindcss.com/docs/display#table) so it will behave like it did before the flex attributes were added.

```CSS
.tr-class {
  @apply flex flex-col mb-4 sm:table-row
}
```
Now it expands out to the rows as before! For the last touch, we need to re-implement the rounding of the table edges, this time with breakpoints. 

As an aside, this is why, when you are able, it is highly recommended that you [build for your smallest screens first](https://tailwindcss.com/docs/responsive-design#working-mobile-first) and then work your way up. It’s a bit trickier going the other way around, but we opted for that approach since we only had one set of designs.

We first remove the border radius from  the top and bottom when it hits the `sm` breakpoint with `sm:last:rounded-b-none sm:first:rounded-tl-lg`. We then add our original radii.

```CSS
.td-class {
  @apply px-4 py-3 bg-gray-200 first:rounded-t-lg last:rounded-b-lg sm:first:rounded-t-none sm:last:rounded-b-none sm:first:rounded-tl-lg sm:first:rounded-bl-lg sm:last:rounded-tr-lg sm:last:rounded-br-lg
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j698w0x9wapk5btcsql7.png)

And that’s it! A screen reader friendly and responsive list of data. To play around with the styling and see the final state of the code, you can check out this [Tailwind Play](https://play.tailwindcss.com/75495oZnND). 

## Resources
[Tailwind Components](https://tailwindcomponents.com/component/mobile-responsive-table)
[Tailwind Docs](https://tailwindcss.com/)