Search code examples
htmlcsshtml-tablecss-positionsticky

HTML Table with Border Radius & Sticky Header


I have a HTML <table> with a border-radius and a sticky header using position: sticky that looks like this:

https://codepen.io/muhammadrehansaeed/pen/OJpeeKP

enter image description here

However, when scrolling with the sticky header, the table rows stick out where the rounded corner of the sticky header lives. See top left of this image:

table row borders displayed in area cut out by border radius of sticky header

Is there a way I can maintain the rounded corners when scrolling down with the sticky header or remove the sticky header when the header becomes sticky and moves down from its original position? Ideally, I'd like a CSS only solution.


Solution

  • As Ivan suggested it seems that using pseudo elements for covering unwanted borders below header is (surprisingly) the only viable option. I'd advise using pseudos not only for covering "outer" area but even for drawing the arcs and filling the "inner" area. It is possible to do so using stacked backgrounds. Applied to original code:

    /* 
    § Additions / Changes
    */
    table thead th {
      position: relative;
    }
    
    /* Pseudos exceeding header boundary by border width; with rectangle covering half circle and rest of height */
    
    table thead th:last-child::after,
    table thead th:first-child::before {
      content: "";
      position: absolute;
      top: 0;
      bottom: 0;
      left: calc(-1 * var(--global-border-width-1));
      width: var(--global-border-radius);
      background-image:
        linear-gradient(to bottom, 
          transparent var(--global-border-radius),
          var(--global-title-color) 0),
        radial-gradient(circle at var(--global-border-radius) var(--global-border-radius),
          var(--global-title-color) var(--global-border-radius),
          var(--global-content-background-color) 0);
      background-position: top left;
      background-size:
        var(--global-border-diameter) 100%,
        var(--global-border-diameter) var(--global-border-diameter);
      background-repeat: no-repeat;
    }
    
    table thead th:last-child::after {
      left: auto;
      right: calc(-1 * var(--global-border-width-1));
      background-position: top right;
    }
    
    /*
    § Declarations and original style
    */
    
    html {
      --global-content-background-color: white;
      --global-title-color: black;
      --global-background-color: lightblue;
      --global-border-color: black;
      --global-border-radius: 20px;
      --global-border-width-1: 10px;
      --global-space-fixed-2: 10px;
      --global-space-fixed-3: 15px;
      --global-border-diameter: calc(2 * var(--global-border-radius));
      background-color: var(--global-content-background-color);
    }
    
    table {
      color: var(--global-title-color);
      background-color: var(--global-content-background-color);
      border-collapse: separate;
      border-color: var(--global-title-color);
      border-style: solid;
      border-radius: var(--global-border-radius);
      border-width: 0 var(--global-border-width-1) var(--global-border-width-1) var(--global-border-width-1);
      border-spacing: 0;
      width: 100%;
    }
    
    table thead {
      position: sticky;
      top: 0;
      z-index: 10;
    }
    
    table thead th {
      color: var(--global-background-color);
      background-color: var(--global-title-color);
      padding: var(--global-space-fixed-2) var(--global-space-fixed-3);
      vertical-align: bottom;
    }
    
    table tbody td {
      border-top: var(--global-border-width-1) solid var(--global-border-color);
      padding: var(--global-space-fixed-2) var(--global-space-fixed-3);
      vertical-align: top;
    }
    
    table tbody tr:last-child td:first-child {
      border-bottom-left-radius: var(--global-border-radius);
    }
    
    table tbody tr:last-child td:last-child {
      border-bottom-right-radius: var(--global-border-radius);
    }
    
    /*
    § Unrelated / demo
    */
    
    * {
      scroll-margin-top: calc(var(--global-space-fixed-2) * 4 + 1rem);
      /* = height of sticky thead + top padding of cell, provided text in thead does not wrap */
      scroll-margin-bottom: 1em;
    }
    
    td {
      height: 60vh;
    }
    
    td a {
      float: right
    }
    
    tr:last-child td {
      vertical-align: bottom;
    }
    
    a[name]:empty::before {
      content: attr(name);
    }
    
    th:not(#\0):hover::before {
      background-image: linear-gradient(to bottom, transparent var(--global-border-radius), #0F08 0), radial-gradient(circle at center, #00F8 var(--global-border-radius), #F2F4 0);
      background-position: top left;
      background-size: var(--global-border-diameter) 100%, var(--global-border-diameter) var(--global-border-diameter);
    }
    <table>
      <thead>
        <tr>
          <th>Fake non-transparent "border-radius"</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>
            <a href="#⬆️" name="⬇️"></a> For fixed header</td>
        </tr>
        <tr>
          <td>
            <a href="#⬇️" name="⬆️"></a> Using CSS stacked background images in pseudo elements</td>
        </tr>
      </tbody>
    </table>