How do you make a spreadsheet-like interface responsive without the use of any JavaScript? This is the question I’ve been asking myself all week as I’ve been working on a new project and trying to figure out how to make the simplest spreadsheet possible. I wanted to avoid using a framework and instead, I decided to experiment with some new properties in order to use nothing but a light touch of CSS.
Spoilers! This is what I’ve come up with so far (oh and please note that this demo currently works in the latest version of Chrome). Try scrolling around a little bit:
See the Pen A Simple Responsive Spreadsheet – Final by Robin Rendle (@robinrendle) on CodePen.
Notice how the first column sticks to the left and the heading sticks to the top of the spreadsheet? This lets us scan lots of data without having to keep scrolling to figure out which column or row we’re in — in a lot of interfaces like this it’s pretty easy to get lost.
So how did I go about making this thing? Let’s jump in!
Adding the markup
First we need to add our markup for the table and, just to make sure that this example is as realistic as possible, we’re going to add a lot of rows and columns here:
See the Pen A Simple Responsive Spreadsheet – 1st by Robin Rendle (@robinrendle) on CodePen.
There’s nothing really complex going on. We just have a regular ol’ table with a <thead>
and a <tbody>
, but we do wrap the whole table in the table-wrapper
div which I’ll explain in just a little bit.
Next, we’ll add basic styling to that wrapper element to move it into the center of the page and also give it a max-width. We also need to make sure that the .table-wrapper
has overflow set to scroll, although at larger screen sizes we won’t need that just yet:
body {
display: flex;
font-family: -apple-system;
font-size: 90%;
color: #333;
justify-content: center;
}
.table-wrapper {
max-width: 700px;
overflow: scroll;
}
See the Pen A Simple Responsive Spreadsheet – 2nd by Robin Rendle (@robinrendle) on CodePen.
Nifty! Now we can add styles for the first column of our table and the thead
element as well as basic styling for each of the table cells:
table {
border: 1px solid #ddd;
border-collapse: collapse;
}
td, th {
white-space: nowrap;
border: 1px solid #ddd;
padding: 20px;
}
See the Pen A Simple Responsive Spreadsheet – 3 by Robin Rendle (@robinrendle) on CodePen.
The problem here is that we’ve now made a pretty inaccessible table; although we can scroll around in the spreadsheet we can’t read which column or row is associated to which bit of data. This can lead to a table that is almost completely illegible and if we were to populate this with real data then it would be even worse:
![](https://i0.wp.com/css-tricks.com/wp-content/uploads/2017/11/Screenshot-2017-11-24-10.26.54.png?ssl=1)
position: sticky to the rescue!
position: sticky
is a wonderfully handy CSS trick that I’ve started experimenting with a great deal lately. It lets you stick child elements to their parent containers so that as you scroll around the child element is always visible. And this is exactly what we need here for the first column and the heading of our table element.
We can use this relatively new feature with CSS like this:
// The heading of our table
th {
background-color: #eee;
position: sticky;
top: -1px;
z-index: 2;
// The first cell that lives in the top left of our spreadsheet
&:first-of-type {
left: 0;
z-index: 3;
}
}
// The first column that we want to stick to the left
tbody tr td:first-of-type {
background-color: #eee;
position: sticky;
left: -1px;
z-index: 1;
}
This z-index values are important here because we want the header to overlap the first left hand column that will also be sticky. However! We also want that empty cell at the top left to overlap both our header and our left hand column, like this:
See the Pen A Simple Responsive Spreadsheet – Final by Robin Rendle (@robinrendle) on CodePen.
But there we have it! A simple responsive spreadsheet where you can view both the heading and the first column no matter where you are in the table. Although, it’s worth noting that your mileage may vary. position: sticky
has relatively patchy support right now and so it’s worth thoroughly testing before you start using it. Or you could use something like Stickybits that would act as a lightweight polyfill.
Also, if you need to dig into tables in more depth then we’ve made a rather handy Complete Guide to the Table Element.
Sadly, that does not work in Firefox. I did a similar example in Javascript using
transform
– https://codepen.io/monochromer/pen/YyydwyI’ve been using position: absolute; to stick a table column.
I’ve been looking for this for so long! I build a lot of event websites and this is perfect for their timetables.
Can’t wait to start playing with this!
This is cool, but there’s one little tweak people miss: It’s totally valid (and encouraged where appropriate) to use a
<th>
in<tbody>
. It’s good for cells that label the row, such as the first column of your examples. You’ll want to indicate that it should apply to the row, not the column, so add thescope="row"
attribute (i.e.<th scope="row">Anna</th>
). The nice thing is you’ll be able to re-use a lot of the existingth
styling you’ve already got.Oh this is great! Thanks Mark – I had no idea you could do that.
Firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=975644
They’re almost done making
sticky
work for<th>