cssgoogle-chromehtml-tablebootstrap-5fixed-header-tables

How to get rid of artifacts displayed when a table with a sticky header is scrolled?


Using Bootstrap 5, I wrote an HTML table with a scrollable content and a "sticky" header. I also used Bootstrap CSS classes to stylize the table.

The snippet below shows a minimal example:

html,
body {
  height: 100%;
}

body {
  font-size: 1.5em
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" />
<div id="root" class="container d-flex flex-column h-100" style="border:1px solid black;">
  <div class="mb-3 text-center"> Header </div>
  <div class="flex-grow-1 overflow-auto">
    <table class="table table-bordered table-striped table-hover">
      <thead class="table-dark sticky-top">
        <tr>
          <th scope="col">Header 1</th>
          <th scope="col">Header 2</th>
          <th scope="col">Header 3</th>
          <th scope="col">Header 4</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>Content 1</td>
          <td>Content 2</td>
          <td>Content 3</td>
          <td>Content 4</td>
        </tr>
        <tr>
          <td>Content 1</td>
          <td>Content 2</td>
          <td>Content 3</td>
          <td>Content 4</td>
        </tr>
        <tr>
          <td>Content 1</td>
          <td>Content 2</td>
          <td>Content 3</td>
          <td>Content 4</td>
        </tr>
        <tr>
          <td>Content 1</td>
          <td>Content 2</td>
          <td>Content 3</td>
          <td>Content 4</td>
        </tr>
      </tbody>
    </table>
  </div>
  <div class="mt-3 mb-3 text-center"> Footer </div>
</div>

But when the content is scrolled with Chrome:

Padding between the header and the top border

Did I miss something, or is this a known behavior?


Solution

  • position: sticky is applied to <thead>, but Bootstrap adds borders to <td>.

    This probably happens because Chrome renders the borders on the elements below and strangely ignores z-index.

    Try forcibly removing the borders from <thead>:

    thead tr, thead th {
      border: none;
    }
    

    And instead, use a box-shadow for the header:

    thead th {
      box-shadow: -1px 1px 2px 0px rgb(159 72 134 / 90%) !important;
    }
    

    html,
    body {
      height: 100%;
    }
    
    body {
      font-size: 1.25em
    }
    
    
    thead tr, thead th {
      border: none;
    }
    
    thead th {
          box-shadow: -1px 1px 2px 0px rgb(159 72 134 / 90%) !important;
    }
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" />
    
    
    
    <div id="root" class="container d-flex flex-column h-100" style="border:1px solid black;">
      <div class="mb-3 text-center"> Header </div>
      <div class="flex-grow-1 overflow-auto">
        <table class="table table-bordered table-striped table-hover">
          <thead class="table-dark sticky-top">
            <tr>
              <th scope="col">Header 1</th>
              <th scope="col">Header 2</th>
              <th scope="col">Header 3</th>
              <th scope="col">Header 4</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>Content 1</td>
              <td>Content 2</td>
              <td>Content 3</td>
              <td>Content 4</td>
            </tr>
            <tr>
              <td>Content 1</td>
              <td>Content 2</td>
              <td>Content 3</td>
              <td>Content 4</td>
            </tr>
            <tr>
              <td>Content 1</td>
              <td>Content 2</td>
              <td>Content 3</td>
              <td>Content 4</td>
            </tr>
            <tr>
              <td>Content 1</td>
              <td>Content 2</td>
              <td>Content 3</td>
              <td>Content 4</td>
            </tr>
          </tbody>
        </table>
      </div>
      <div class="mt-3 mb-3 text-center"> Footer </div>
    </div>