3

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,816
  • 57
  • 73
  • 92
Muhammad Rehan Saeed
  • 31,909
  • 32
  • 185
  • 290
  • 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

3

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 Kharkovsky
  • 446
  • 3
  • 15
2

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
  • 7,711
  • 2
  • 34
  • 44
0

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
  • 123
  • 1
  • 5
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,026
  • 2
  • 19
  • 30