Deep dive: <tables> with sticky headers and columns
The bare minimum needed to create a table with both sticky header rows and columns.
One day you’ll be asked to create a table that supports horizontal and vertical scrolling, in which case you’ll likely want to keep the 1st row and column visible to users while scrolling. This guide is for you when that day comes.
Part 1. A Basic Table
<table>
<caption>A Basic Table</caption>
<thead>
<tr>
<th>Header</th>
</tr>
</thead>
<tbody>
<tr>
<td>Text</td>
</tr>
</tbody>
</table>
All tables broken down consist of the same html tags. We can add styles on top of this example which will apply to any table you build
Part 2. CSS Reset
/*
1. Remove text indentation from table contents in Chrome and Safari.
https://bugs.chromium.org/p/chromium/issues/detail?id=999088
https://bugs.webkit.org/show_bug.cgi?id=201297
2. Correct table border color inheritance in all Chrome and Safari.
https://bugs.chromium.org/p/chromium/issues/detail?id=935729
https://bugs.webkit.org/show_bug.cgi?id=195016
3. Remove gaps between table borders by default.
*/
table {
text-indent: 0; /* 1 */
border-color: inherit; /* 2 */
border-collapse: collapse; /* 3 */
}
Use the CSS above to normalize the style of your <table>. If you’re looking for a up-to-date CSS Reset, I’m a huge fan of Tailwind’s preflight.css which is pretty similar to a CSS Reset.
Part 3. Make it scroll
We need two things to make an html element scroll
- The parent container needs to specify an
overflow
CSS property. For example,overflow: auto;
- The content needs to not fit in the parent container
<div class="table_wrapper">
<table>...</table>
</div>
.table_wrapper {
overflow: auto; /* (1) turn on overflow */
max-width: 25vw; /* (2) enforce a maximum size that causes overflow */
max-height: 25vh; /* (2) enforce a maximum size that causes overflow */
}
For illustrative purposes, small values were used. However, the max-width
and max-height
can be whatever you want them to be.
Part 4. Make it sticky
Add some more data to the table and then a few CSS properties and you’ve got yourself a table where the 1st row and column remain visible to users while scrolling.
<div class="table_wrapper">
<table>
<caption>A Basic Table</caption>
<thead>
<tr>
<th>Name</th>
<th>Job Description</th>
</tr>
</thead>
<tbody class="tbody">
<tr>
<th scope="row">Chef</th>
<td>Cook stuff</td>
</tr>
</tbody>
</table>
</div>
.table_wrapper {
position: relative;
...
}
thead th {
position: sticky;
top: 0;
}
thead th:nth-of-type(1) {
left: 0;
}
tbody th {
position: sticky;
left: 0;
}
For illustrative purposes, selectors thead th
and tbody th
were used. However, it’d be better to use class names.
Part 5. make it look pretty (ft. a border while scrolling)
Try adding a border to the sticky
element and scroll in the table. You’ll notice the border doesn’t scroll with the content and you lose your border.
Use box-shadow
instead of border
thead th {
z-index: 10;
background-color: white;
/** Use an "inset" box-shadow instead of border-bottom */
box-shadow: inset 0 -1px 0 black;
...
}
thead th:nth-of-type(1) {
z-index: 11;
...
}
tbody th {
z-index: 10;
background-color: white;
/** Use an "inset" box-shadow instead of border-bottom */
box-shadow: inset -1px 0 0 black;
...
}
I’d recommend using this example as a baseline. The styling is very minimal and can easily be improved with usage of thepadding
border
and background-color
CSS properties.
The End
Here’s what our <table> ends up looking like
Additional Resources
- Style up the scrollbar with the
scrollbar-color
,scrollbar-gutter
, andscrollbar-width
colors (https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color). - Snap the scroll position in alignment with table cell containers using the
scroll-snap-align
,scroll-snap-stop
, andscroll-snap-type
property (https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-snap-align).