Building a Mobile-Friendly Pricing Table with Tailwind CSS
There’s no doubt, tables are a true nightmare when it comes to responsive layouts. They often need a lot of space horizontally, which is a problem on phones and tablets where space is limited.
Usually, we end up making the table scroll horizontally. It works, but it’s not the best experience for the user. Making tables work well on small screens is tough. The best solutions are those that reorganize the content specifically for smaller screens, but this is a real challenge when using tables.
However, we recently came across a smart solution from DigitalOcean. They have a pricing table that looks great on any device. We liked it so much that we wanted to make our own mobile-friendly pricing table with Tailwind CSS.
The trick is to make the rows turn into columns on small screens. That way, you can view the content of a single row without having to scroll. We still keep the horizontal scrolling, but we make it better with:
- Sticky headers: These stay fixed at the left while you scroll, so you always know what each column is about
- CSS scroll snapping to create a controlled scroll experience by declaring specific snapp positions
The result is excellent, and focuses on what truly matters: the content. Users can easily compare different plans and make informed decisions. It’s a great example of how to make a table responsive and user-friendly.
Code
<table class="
table-auto
md:w-full
text-sm
snap-x
scroll-pl-4
max-md:grid
max-md:gap-x-12
max-md:overflow-x-scroll
max-md:-mx-4
max-md:px-4
[&_thead]:bg-slate-50
max-md:[&_thead]:contents
max-md:[&_tbody]:contents
[&_tbody_tr]:border-b
[&_tbody_tr]:border-slate-100
max-md:[&_tr]:contents
[&_th]:text-slate-900
[&_th]:font-semibold
[&_td]:text-slate-600
[&_th]:py-3
md:[&_th]:first:pl-3
[md:&_th]:last:pr-3
[&_td]:pb-3
md:[&_td]:pt-3
md:[&_td]:first:pl-3
md:[&_td]:last:pr-3
[&_tr_td:first-child]:font-medium
[&_tr_td:first-child]:text-slate-900
[&_th]:whitespace-nowrap
[&_td]:whitespace-nowrap
max-md:[&_th]:min-w-[60vw]
max-md:[&_td]:min-w-[60vw]
max-md:[&_tr:last-child_td]:min-w-[calc(100vw-2rem)]
[&_th]:text-left
[&_th]:group-last:text-right
[&_td]:text-left
md:[&_td:last-of-type]:text-right
max-md:[&_th]:sticky
max-md:[&_th]:left-0
[&_td]:snap-start
max-md:[&_td]:border-b
max-md:[&_td:last-of-type]:border-none
[&_td]:border-slate-100
">
<thead>
<tr>
<th class="max-md:row-start-1 max-md:col-start-1">Plan</th>
<th class="max-md:row-start-3 max-md:col-start-1">Storage</th>
<th class="max-md:row-start-5 max-md:col-start-1">Bandwidth</th>
<th class="max-md:row-start-7 max-md:col-start-1">Emails</th>
<th class="max-md:row-start-9 max-md:col-start-1">SSD</th>
<th class="max-md:row-start-11 max-md:col-start-1">$/hr</th>
<th class="max-md:row-start-13 max-md:col-start-1">$/mo</th>
<th class="max-md:row-start-[15] max-md:col-start-1"><span class="sr-only">Buy</span></th>
</tr>
</thead>
<tbody>
<tr>
<td class="max-md:row-start-2 max-md:col-start-1">Basic</td>
<td class="max-md:row-start-4 max-md:col-start-1">20GB</td>
<td class="max-md:row-start-6 max-md:col-start-1">1TB</td>
<td class="max-md:row-start-8 max-md:col-start-1">10</td>
<td class="max-md:row-start-10 max-md:col-start-1">
<span class="h-5 w-5 flex items-center">
<svg class="shrink-0 fill-slate-400 mr-3" xmlns="http://www.w3.org/2000/svg" width="12" height="12">
<rect width="12" height="2" y="5" rx="1"/>
</svg>
</span>
</td>
<td class="max-md:row-start-12 max-md:col-start-1">0.015</td>
<td class="max-md:row-start-[14] max-md:col-start-1">10</td>
<td class="max-md:row-start-[16] max-md:col-start-1">
<a class="inline-flex justify-center rounded-lg bg-indigo-500 px-2.5 py-1.5 text-sm font-medium text-white shadow-sm shadow-indigo-950/10 hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring focus-visible:ring-indigo-300 dark:focus-visible:ring-slate-600 transition-colors duration-150 group" href="#0">
<span class="md:sr-only">Get Started</span>
<span class="tracking-normal text-indigo-300 md:text-white group-hover:translate-x-0.5 transition-transform duration-150 ease-in-out max-md:ml-1">-></span>
</a>
</td>
</tr>
<tr>
<td class="max-md:row-start-2 max-md:col-start-2">Silver</td>
<td class="max-md:row-start-4 max-md:col-start-2">40GB</td>
<td class="max-md:row-start-6 max-md:col-start-2">2TB</td>
<td class="max-md:row-start-8 max-md:col-start-2">20</td>
<td class="max-md:row-start-10 max-md:col-start-2">
<span class="h-5 w-5 flex items-center">
<svg class="shrink-0 fill-emerald-500 mr-3" xmlns="http://www.w3.org/2000/svg" width="12" height="9">
<path d="M10.28.28 3.989 6.575 1.695 4.28A1 1 0 0 0 .28 5.695l3 3a1 1 0 0 0 1.414 0l7-7A1 1 0 0 0 10.28.28Z"></path>
</svg>
</span>
</td>
<td class="max-md:row-start-12 max-md:col-start-2">0.03</td>
<td class="max-md:row-start-[14] max-md:col-start-2">20</td>
<td class="max-md:row-start-[16] max-md:col-start-2">
<a class="inline-flex justify-center rounded-lg bg-indigo-500 px-2.5 py-1.5 text-sm font-medium text-white shadow-sm shadow-indigo-950/10 hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring focus-visible:ring-indigo-300 dark:focus-visible:ring-slate-600 transition-colors duration-150 group" href="#0">
<span class="md:sr-only">Get Started</span>
<span class="tracking-normal text-indigo-300 md:text-white group-hover:translate-x-0.5 transition-transform duration-150 ease-in-out max-md:ml-1">-></span>
</a>
</td>
</tr>
<tr>
<td class="max-md:row-start-2 max-md:col-start-3">Gold</td>
<td class="max-md:row-start-4 max-md:col-start-3">80GB</td>
<td class="max-md:row-start-6 max-md:col-start-3">4TB</td>
<td class="max-md:row-start-8 max-md:col-start-3">40</td>
<td class="max-md:row-start-10 max-md:col-start-3">
<span class="h-5 w-5 flex items-center">
<svg class="shrink-0 fill-emerald-500 mr-3" xmlns="http://www.w3.org/2000/svg" width="12" height="9">
<path d="M10.28.28 3.989 6.575 1.695 4.28A1 1 0 0 0 .28 5.695l3 3a1 1 0 0 0 1.414 0l7-7A1 1 0 0 0 10.28.28Z"></path>
</svg>
</span>
</td>
<td class="max-md:row-start-12 max-md:col-start-3">0.06</td>
<td class="max-md:row-start-[14] max-md:col-start-3">40</td>
<td class="max-md:row-start-[16] max-md:col-start-3">
<a class="inline-flex justify-center rounded-lg bg-indigo-500 px-2.5 py-1.5 text-sm font-medium text-white shadow-sm shadow-indigo-950/10 hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring focus-visible:ring-indigo-300 dark:focus-visible:ring-slate-600 transition-colors duration-150 group" href="#0">
<span class="md:sr-only">Get Started</span>
<span class="tracking-normal text-indigo-300 md:text-white group-hover:translate-x-0.5 transition-transform duration-150 ease-in-out max-md:ml-1">-></span>
</a>
</td>
</tr>
<tr>
<td class="max-md:row-start-2 max-md:col-start-4">Platinum</td>
<td class="max-md:row-start-4 max-md:col-start-4">160GB</td>
<td class="max-md:row-start-6 max-md:col-start-4">8TB</td>
<td class="max-md:row-start-8 max-md:col-start-4">80</td>
<td class="max-md:row-start-10 max-md:col-start-4">
<span class="h-5 w-5 flex items-center">
<svg class="shrink-0 fill-emerald-500 mr-3" xmlns="http://www.w3.org/2000/svg" width="12" height="9">
<path d="M10.28.28 3.989 6.575 1.695 4.28A1 1 0 0 0 .28 5.695l3 3a1 1 0 0 0 1.414 0l7-7A1 1 0 0 0 10.28.28Z"></path>
</svg>
</span>
</td>
<td class="max-md:row-start-[12] max-md:col-start-4">0.12</td>
<td class="max-md:row-start-[14] max-md:col-start-4">80</td>
<td class="max-md:row-start-[16] max-md:col-start-4">
<a class="inline-flex justify-center rounded-lg bg-indigo-500 px-2.5 py-1.5 text-sm font-medium text-white shadow-sm shadow-indigo-950/10 hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring focus-visible:ring-indigo-300 dark:focus-visible:ring-slate-600 transition-colors duration-150 group" href="#0">
<span class="md:sr-only">Get Started</span>
<span class="tracking-normal text-indigo-300 md:text-white group-hover:translate-x-0.5 transition-transform duration-150 ease-in-out max-md:ml-1">-></span>
</a>
</td>
</tr>
</tbody>
</table>