57

position: 'sticky' landed in Chrome 56, but it makes the border invisible in certain circumstances.

Consider the following example:

table {
  border-collapse: collapse;
}

thead {
  position: sticky;
  top: 0;
}

th {
  background-color: #fff;
  border-right: 5px solid red;
}
<table>
  <thead>
    <tr>
      <th>First</th>
      <th>Second</th>
      <th>Third</th>
    </tr>
  </thead>
</table>

In Chrome 56.0.2924.76, only the last <th>'s border is visible, and this is only when <th> has a background-color specified.

Is this a bug in Chrome?

Playground

Misha Moroshko
  • 166,356
  • 226
  • 505
  • 746
  • 4
    This is reproducible on Firefox 52.0a2. The border reappears on all table headers when `border-collapse: collapse;` is removed. – Sebastian Simon Jan 26 '17 at 20:48
  • beside table elements + position:sticky; in firefox did not work, since it is still experimental i believe it is not fixed yet – G-Cyrillus Jan 26 '17 at 21:01
  • As said, it is still valid in Firefox, but could not reproduce in Chromium. Seems to be fixed there. – phil294 Dec 31 '18 at 00:34
  • here is the bug report for Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1450584 please express yourself there to see it fixed – Fla Apr 23 '20 at 13:54

12 Answers12

78

I faced the same problem. My workaround was to use the :after pseudo element to emulate a bottom border.

th:after{
  content:'';
  position:absolute;
  left: 0;
  bottom: 0;
  width:100%;
  border-bottom: 1px solid rgba(0,0,0,0.12);
}
Eric Guan
  • 15,474
  • 8
  • 50
  • 61
25

seems like to force a reflow will partially help :

table {
  border-collapse: collapse;
}

thead {
  position: sticky;
  top: 0;
}

th {
  border-right: 5px solid red;
  transform:scale(0.999);
}
  <table>
    <thead>
      <tr>
        <th>First</th>
        <th>Second</th>
        <th>Third</th>
      </tr>
    </thead>
  </table>

background-clip seems also efficient and harmless:

table {
  margin-top: 1em;
  border-collapse: collapse;
}

thead {
  position: sticky;
  top: 0;
}

th {
  border-right: 5px solid red;
  background:white; 
  background-clip: padding-box;
}
  <table>
    <thead>
      <tr>
        <th>First</th>
        <th>Second</th>
        <th>Third</th>
      </tr>
    </thead>
  </table>
G-Cyrillus
  • 101,410
  • 14
  • 105
  • 129
  • All this does is make the existing border (which is hidden behind the element) visible. If your table is inside a fixed-height scrollable div the border won't stay attached when you start scrolling it. – Yay295 Nov 15 '22 at 17:14
22

if table contains border around columns and we add sticky position, when we scroll the table show overlapping effect to remove this effect and retains border we need to remove border and add outline instead of border

table tr th{
  outline: 1px solid #e9ecef;
  border:none;
  outline-offset: -1px;
}
shahida
  • 327
  • 3
  • 9
  • if table contains border around columns and we add sticky position, when we scroll the table show overlapping effect to remove this effect and retains border we need to remove border and add outline instead of border – shahida May 07 '18 at 07:38
  • 1
    Definitely the best answer (at least for Chrome). @shahida I suggest you move your comment that explains why it works within the body of your answer. – Balazs Zsoldos Sep 28 '18 at 10:49
  • 9
    This is extremely limited because it can only draw a line on *all* sides and the lines will not collapse with adjacent cells. Looks ugly. Can't use this. – ygoe Nov 29 '18 at 14:02
  • Changed outline to 2px in order to avoid a move of the header in my case and this solution works like a charm. – Marc Roussel May 23 '21 at 04:47
  • An `outline-offset` of `-0.5px` makes the "borders" overlap. – Yay295 Nov 15 '22 at 16:33
10

I have solved with shadow

table tr th {
  position: -webkit-sticky;
  position: sticky;
  top: -1px;
  z-index: 2;
  background-color: white;
  -moz-box-shadow: 0 0 1px -1px #ccc;
  -webkit-box-shadow: 0 0 1px -1px #ccc;
  box-shadow: 0 0 1px -1px #ccc;
}
Project Mayhem
  • 439
  • 5
  • 12
5

The next example currently works well under Chrome (65) and Firefox (59).

The SCSS code illustrates better the relationship between values. You can set the desired values by change the variables.

SCSS:

table {

    &.sticky-table-head {

        // Variables

        $border-width: 2px;

        $head-background-color: #ded;
        $head-border-color: #8a8;

        $background-color: #f8fff8;
        $border-color: #cdc;

        $color: #686;

        // Declarations

        margin-bottom: 1em;
        border-collapse: collapse;
        background-color: $background-color;
        color: $color;

        &:last-child {
            margin-bottom: 100vh;
        }

        th,
        td {
            border: $border-width solid $border-color;
        }

        thead {

            th {
                position: sticky;
                top: ($border-width / 2);
                background-color: $head-background-color;
                outline: $border-width solid $head-border-color;
                outline-offset: (- $border-width / 2);
            }
        }
    }
}

HTML and compiled CSS:

table.sticky-table-head {
 margin-bottom: 1em;
 border-collapse: collapse;
 background-color: #f8fff8;
 color: #686;
}

table.sticky-table-head:last-child {
 margin-bottom: 100vh;
}

table.sticky-table-head th,
table.sticky-table-head td {
 border: 2px solid #cdc;
}

table.sticky-table-head thead th {
 position: -webkit-sticky;
 position: sticky;
 top: 1px;
 background-color: #ded;
 outline: 2px solid #8a8;
 outline-offset: -1px;
}
<div>
 <!-- First table -->
 <table class="sticky-table-head">
  <thead>
   <tr>
    <th>Lorem</th>
    <th>Ipsum</th>
    <th>Dolor sit amet</th>
   </tr>
  </thead>
  <tbody>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
  </tbody>
 </table>
 <!-- Second table -->
 <table class="sticky-table-head">
  <thead>
   <tr>
    <th>Lorem</th>
    <th>Ipsum</th>
    <th>Dolor sit amet</th>
   </tr>
  </thead>
  <tbody>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
   <tr>
    <td>ipsum</td>
    <td>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</td>
    <td>sit</td>
   </tr>
  </tbody>
 </table>
</div>
3

Try this instead:

background-clip: padding-box;
1

Use ::after pseudo selector to emulate right border

Example:

th::after {
  content: '';
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  border-right: 1px solid #e7e7e7;
}
Ayan
  • 8,192
  • 4
  • 46
  • 51
0
thead::after {
  content: '';
  display: block;
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  height: 1px;
  background-color: #333333;
}

I fixed this same problem with a pseudo element instead of a border. Not sure of why this happens in the first place, though.

0

I'm using only horizontal borders in my page so this may need to be adapted to your particular case. By using a half-transparent background colour for the table cells, I've discovered that it's the bottom end of the background that overlays the horizontal border. I guess it'll be the same with the right end for the vertical border. To avoid the border overlapping, I've set a linear gradient that fills everything but spares the last pixel. Unfortunately, this only works in Firefox and Chrome but not in Edge.

td:first-child {
  position: sticky;
  left: 0px;
  z-index: 2;
  background: linear-gradient(to bottom, white, white calc(100% - 1px),
                              transparent calc(100% - 1px), transparent);
}

Edge does paint a linear gradient but from white at the top to transparent at the bottom, ignoring the hard change at the stop at 100% - 1px.

ygoe
  • 18,655
  • 23
  • 113
  • 210
0

Another option is to wrap your content in an element and put the border on that:

.th-inner {
  padding: 10px;
  border-bottom: 1px solid #ccc;
}
<th>
  <div class="th-inner">your content</div>
</th>

However I found that putting the border on the th worked without border-collapse: collapse on the table, so I went for that.

double-beep
  • 5,031
  • 17
  • 33
  • 41
Dominic
  • 62,658
  • 20
  • 139
  • 163
0

html {
  padding: 0px;
  margin: 0px;
}

body {
  padding: 0px;
  margin: 0px;
  width: 100%;
}

th,
td {
  padding: 10px;
  border: 0.1px solid #e8e0e0;
  padding-right: 10px;
}

th {
  text-align: center;
  background: #f3f3f3;
  background-clip: padding-box;
}

thead th:first-child {
  left: 0;
  z-index: 1;
}

tbody th:first-child {
  text-align: center;
  position: -webkit-sticky;
  /* for Safari */
  position: sticky;
  left: 0;
}

tbody th,
thead th:first-child {
  width: 30px;
  min-width: 30px;
  max-width: 30px;
  word-break: break-all;
}

.fixed_header {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
}


/* fixed header */

thead th {
  /* for Safari */
  text-align: center;
  position: -webkit-sticky;
  position: sticky;
  top: 0;
}

.fixed_header th,
.fixed_header td {
  padding: 10px;
  width: 90px;
}

.table_container {
  position: relative;
  width: 100%;
  min-height: 500px;
  overflow: auto;
  background: cornsilk;
}
<table class="fixed_header">
  <thead>
    <tr>
      <th></th>
      <th>Col 1</th>
      <th>Col 2</th>
      <th>Col 3</th>
      <th>Col 4</th>
      <th>Col 5</th>
      <th>Col 1</th>
      <th>Col 2</th>
      <th>Col 3</th>
      <th>Col 4</th>
      <th>Col 5</th>
      <th>Col 1</th>
      <th>Col 2</th>
      <th>Col 3</th>
      <th>Col 4</th>
      <th>Col 5</th>
      <th>Col 1</th>
      <th>Col 2</th>
      <th>Col 3</th>
      <th>Col 4</th>
      <th>Col 5</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>1</th>
      <td>row 1-1</td>
      <td>row 1-2</td>
      <td>row 1-3</td>
      <td>row 1-4</td>
      <td>row 1-1</td>
      <td>jhhhh-2</td>
      <td>row 1-3</td>
      <td>row 1-4</td>
      <td>row 1-1</td>
      <td>row 1-2</td>
      <td>row 1-3</td>
      <td>row 1-4</td>
      <td>row 1-1</td>
      <td>row 1-2</td>
      <td>row 1-3</td>
      <td>row 1-4</td>
      <td>row 1-1</td>
      <td>row 1-2</td>
      <td>row 1-3</td>
      <td>row 1-4</td>
    </tr>

  </tbody>
</table>
</div>
double-beep
  • 5,031
  • 17
  • 33
  • 41
Balaji
  • 9,657
  • 5
  • 47
  • 47
0

box-shadow: inset 0 0 0px 2px #555; instead of border: solid 2px #555; will work. But that is indeed a bug and should be fixed by the browsers.

Fla
  • 536
  • 6
  • 23