Priyanshu Nayan

Tables are hard

Recently at work, I got to fix two nasty CSS bugs. We had a table which was horizontally scrollable. We wanted to keep the border of the the cells of first column to be present when it was being scrolled, and to give the user visual feedback that the table is currently in scrolled state and that some columns are hidden behind the first column, we wanted to show a right shadow on the all the cells of first column. This simple requirement turned out tricky. Tables are not so straight forward after all.

Borders in sticky positioned table cells

There could be lots of cases where we would want the first column of the table to stick when table is horizontally scrolled. And since borders are one of the most common separators between the table cells, so one might want them there. This is fairly easy to attain in chrome and safari. Firefox refuses to correctly add border on sticky positioned cell! It turns out to be an 11 years old issue1. The gist of the issue is that when a cell is sticky positioned, the background paints over the border, making it visually hidden. It looks something like this in firefox:

no bottom border in firefox

In the following sandbox, we have the code for the above table with border collapse model. The cells of first column are sticky. If you are viewing this on chrome or safari, you should be able to scroll and see the border first cell being there. In firefox, it disappears.

There are two fixes to it, which works well in Firefox and continues to work well in safari and chrome.

1. Clip the background till the padding box.

This fix is aimed at the core of the issue where background paints over the border too. The CSS syntax: background-clip: padding-box is all that we need for the all the cells which are sticky positioned.

tbody tr td:first-child {
  position: sticky;
  left: 0;
  background-color: white;
  background-clip: padding-box;
}

2. Have the border-collapse as separate.

Having the CSS property border-collapse: separate also seems to fix the issue(with other cosmetic CSS changes). But this might not be feasible, thanks to the cascading and reusability nature of CSS selectors. Overriding things at the component level, sooner or later is going to make things complex and less maintainable.

table {
  ...styles;
  border-collapse: separate;
}

It was interesting to see how one simple requirement could lead to bad UX in some browsers and can even harm business.

Box shadow on the right side for the sticky positioned element

Inline with earlier requirement, we needed to give user visual feedback that the table is scrolled and there is something behind the first column which is sticking at the top. Normal box-shadow turned out work just fine in Firefox and Safari. But Chrome refuses to show box-shadow for such elements. For simplicity, I am applying box shadow on all sides and at all times. In practical we would want this to apply only when table is scrolled.

If you are viewing this page in chrome, you will see absolutely no box shadow at all, even though we have CSS for that. It looks like this in firefox:

firefox with box shadow

According to the spec2:

Outer shadows have no effect on internal table elements in the collapsing border model.

Chrome seems to be following this. But Safari and Firefox display box shadow for table cells even when border model is collapsing. Weird!

The fix that I came up with was to use inner box shadow. We use inner box shadow by appending inset keyword in the front or at the back of the syntax(liberal grammar I know). This approach worked out best for us and we now also have the assurance of the CSS spec(not that it matters!). It worked out fine on Firefox, Chrome and Safari.

tbody tr td:first-child {
  position: sticky;
  left: 0;
  background-color: white;
  box-shadow: -1px 0 2px 0 red inset;
}

This is how our beloved table works now across all browsers:

GIF showing table

As a frontend engineer, it’s a part of our job to make sure it works consistently across all browsers. This is the easy part. The difficult part is how different browsers behave differently and when they decide to diverge from the CSS spec. In fast paced work environments, making sure everything works everywhere all the time becomes even more difficult due to this. Web is not perfect, I guess, yet.


  1. https://bugzilla.mozilla.org/show_bug.cgi?id=688556

  2. https://drafts.csswg.org/css-backgrounds/#shadow-layers

I post more frequently on Twitter. Follow me there to see what I am upto these days!