5

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.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Muhammad Rehan Saeed
  • 35,627
  • 39
  • 202
  • 311
  • 1
    just tried you pen - you could calculate the current position of the elements and once the rows pass into the sticky header you apply the same radius to them an vice versa - a bit of a hack but why not ;) (or a display:none so they hide behind the sticky corner) – iLuvLogix Jun 24 '21 at 16:06
  • 1
    another option would be to look at the answer to this [question](https://stackoverflow.com/questions/57166162/table-headers-positionsticky-and-border-issue) – iLuvLogix Jun 24 '21 at 16:12
  • 1
    in case you need to other direction here is a relevant question: https://stackoverflow.com/q/62129021/8620333 – Temani Afif Jun 29 '21 at 15:14

4 Answers4

5

you can hide some parts of borders using pseudo-elements:

table thead th:first-child::before, 
table thead th:last-child::after {
    width: 1px;
    height: 5px;
    background: white;
    content: "";
    display: block;
    position: absolute;
    top: 0px;
}
table thead th:first-child::before {
    left: -1px;
}
table thead th:last-child::after {
    right: -1px;
}
Ivan Kharkivskyi
  • 569
  • 1
  • 4
  • 22
  • This did not work for me in firefox, the rectangle was on top of everything. Also if the whole table has a box shadow, that shadow will scroll up and be visible a little bit around the header – Christian Apr 02 '23 at 08:01
3

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>
myf
  • 9,874
  • 2
  • 37
  • 49
  • If I add this style to your table: element { box-shadow: 0 7px 14px 0 rgb(21, 50, 188), 0 3px 6px 0 rgba(0,0,0,0.07); }, then there will still be artifacts when scrolling up. Do you also have a solution for this? I wish there was a way to clip the table cells that scroll under the sticky header away completely, but I can't find anything – Christian Apr 02 '23 at 08:05
  • 1
    @Christian I don't think it would be humanly possible/feasible to make this crude solution work with shadows, background images or transparency in general. It is what it is: a workaround that relies on completely obscuring unwanted content with something else. (Unfortunately, even antialiasing (smoothing) can be visibly different with those fake gradient backgrounds than when done with real border edges). – myf Apr 03 '23 at 14:12
0

Just remove the border from the table and add borders left and right for first and last table cells in the table body:

tbody td:first-child {
  border-left: 1px solid black;
}
tbody td:last-child {
  border-right: 1px solid black;
}
tbody tr:last-child td{
  border-bottom: 1px solid black;
}
tbody tr:last-child td:first-child {
  border-bottom-left-radius: 2px;
}
tbody tr:last-child td:last-child {
  border-bottom-right-radius: 2px;
}

With the proper indentation, nesting and your variables, of course!

Looks quite ugly styling in terms of using many pseudoselectors, but seems to be a fix for your issue.

n1kkou
  • 3,096
  • 2
  • 21
  • 32
-1

Not sure if you are familiar with jquery, if yes then you can dynamically change the border-top-left-radius: equal to the radius of sticky header whenever you scroll the content, but it's a bit tricky for a newbie to jquery/JS.

Secondly, you can enclose your sticky header to a parent (say class="parent"), and give that parent background as white with no border. And keep the sticky header's border radius round as they are. Hence when you'll scroll the content it will be below that parent (say class="parent"). To make sure that the parent appears above the row, you can give z-index: 10

NAVNEET CHANDAN
  • 268
  • 3
  • 7