3
votes

When assigning a background color of a table cell on hover, the box shadow is covered.

I have found several questions asking something similar, and my understanding is this is correct behavior: background colors do overlay box shadows.

However, I have seen several answers on here using a "mask" (new element) or :before and :after. The "mask" solution won't work for me as I cannot edit the HTML.

I first tried using borders instead of box shadows, but that moved the rows up and down due to varying heights.

Is this possible? Row should highlight on hover, cell should highlight a different color on hover, row should have 4px border on hover but cells should not shift up/down on hover.

table {
  border-collapse: collapse;
}

table tr:hover {
  box-shadow: inset 0px 0px 0px 4px blue;
  background: red;
}

table tr td {
  border: 1px solid black;
  padding: 5px;
}

table tr td:hover {
  background: yellow;
}

table tr td:hover::before {
  content: '';
  display: block;
  box-shadow: inset 0 0 0 4px blue;
  position: relative;
  top: -5px;
  height: 4px;
}

table tr td:hover::after {
  content: '';
  display: block;
  box-shadow: inset 0 0 0 4px blue;
  position: relative;
  bottom: -5px;
  height: 4px;
}
<table>
  <tbody>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
  </tbody>
</table>

JS Fiddle: https://jsfiddle.net/jennifergoncalves/guqtj4y0/26/

3

3 Answers

3
votes

You didn't specify how you want to accomplish the borders. In your title you box-shadow, but in your description you try to do it with borders:

Row should highlight on hover, cell should highlight a different color on hover, row should have 4px border on hover but cells should not shift up/down on hover.

I didn't do it with an actual border. Instead, I set an outline and just set an outline-offset. This should be what you're looking for.

table {
  border-collapse: collapse;
}

table tr td {
  border: 1px solid black;
  padding: 5px;
}

table tr:hover {
  background: yellow;
  outline: 4px solid blue;
  outline-offset: -4px;
}

table tr td:hover {
  background: red;
}
<table>
  <tbody>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
  </tbody>
</table>

This solution is very convenient because you can pretty easily style an outline. If you, for example, want to add an outline to the specific table cell that is hovered, just add the outline to the cell with the according pseudo-class and you should be ready to go.

0
votes

So to apply the box shadow in this fashion (inset) to your table row you need to modify the display of <tr> and then apply a little bit of ::after CSS (which you already had pretty much done).

It's really just a matter of applying them to the correct HTML elements and adding a bit of extra CSS like pointer-events: none to ensure that your other hover events are honored.

table {
  border-collapse: collapse;
}

tr { 
  display: block;
  margin-bottom: -1px;
  position: relative;
  transition: box-shadow, background 0.3s ease-in-out;
}

tr:hover {
  background: red;
}

tr:hover::after {
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  box-shadow: inset 0 0 0 4px blue;
  top: 0;
  pointer-events: none;
}

td {
  border: 1px solid black;
  padding: 5px;
}

td:hover {
  background: yellow;
}
<table>
  <tbody>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
  </tbody>
</table>

One thing I would note; if you wanted the box shadow to appear around the table row instead of inset, you could achieve that effect without needing to use pseudo elements at all. Simply applying the box shadow to tr:hover (without the inset declaration) and you're done!

0
votes

You can consider multiple background to achieve this:

table {
  border-collapse: collapse;
}

table tr:hover {
  box-shadow: inset 0px 0px 0px 4px blue;
  background: red;
}

table tr td {
  border: 1px solid black;
  padding: 5px;
}

table tr td:hover {
  background:
   linear-gradient(blue,blue) top,
   linear-gradient(blue,blue) bottom,
   yellow;
  background-size:100% 4px;
  background-repeat:no-repeat;
}

table tr td:first-child:hover {
  background:
   linear-gradient(blue,blue) top,
   linear-gradient(blue,blue) bottom,
   linear-gradient(blue,blue) left,
   yellow;
  background-size:100% 4px,100% 4px,4px 100% ;
  background-repeat:no-repeat;
}
table tr td:last-child:hover {
  background:
   linear-gradient(blue,blue) top,
   linear-gradient(blue,blue) bottom,
   linear-gradient(blue,blue) right,
   yellow;
  background-size:100% 4px,100% 4px,4px 100% ;
  background-repeat:no-repeat;
}
<table>
  <tbody>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
  </tbody>
</table>

You can optimize like below:

table {
  border-collapse: collapse;
}

table tr:hover {
  box-shadow: inset 0px 0px 0px 4px blue;
  background: red;
}

table tr td {
  border: 1px solid black;
  padding: 5px;  
  background:
   linear-gradient(blue,blue) top,
   linear-gradient(blue,blue) bottom,
   linear-gradient(blue,blue) left,
   linear-gradient(blue,blue) right,
   linear-gradient(yellow,yellow);
  background-repeat:no-repeat;
  background-size:0 0;
}

table tr td:hover {
  background-size:100% 4px,100% 4px, 0 0,0 0,auto;
}

table tr td:first-child:hover {
  background-size:100% 4px,100% 4px, 4px 100%,0 0,auto;
}
table tr td:last-child:hover {
  background-size:100% 4px,100% 4px,0 0,4px 100%,auto;
}
<table>
  <tbody>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
  </tbody>
</table>

Another idea with less gradient:

table {
  border-collapse: collapse;
}

table tr:hover {
  box-shadow: inset 0px 0px 0px 4px blue;
  background: red;
}

table tr td {
  border: 1px solid black;
  padding: 5px;  
  background-image:linear-gradient(yellow,yellow);
  background-repeat:no-repeat;
  background-size:0 0;
}

table tr td:hover {
  background-size:100% calc(100% - 8px);  
  background-position:center;
}

table tr td:first-child:hover {
  background-size:calc(100% - 4px) calc(100% - 8px);
  background-position:right;
}
table tr td:last-child:hover {
  background-size:calc(100% - 4px) calc(100% - 8px);
  background-position:left;
}
<table>
  <tbody>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
  </tbody>
</table>

You can also consider inset box-shadow on the td elements:

table {
  border-collapse: collapse;
}

table tr:hover {
  box-shadow: inset 0px 0px 0px 4px blue;
  background: red;
}

table tr td {
  border: 1px solid black;
  padding: 5px;  
}

table tr td:hover {
  background:yellow;
  box-shadow:
    0 4px 0 blue inset,
    0 -4px 0 blue inset;
}

table tr td:first-child:hover {
  box-shadow:
    4px 0 0 blue inset,
    0 4px 0 blue inset,
    0 -4px 0 blue inset;
}
table tr td:last-child:hover {
  box-shadow:
    -4px 0 0 blue inset,
    0 4px 0 blue inset,
    0 -4px 0 blue inset;
}
<table>
  <tbody>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
    <tr>
      <td>AAA</td>
      <td>BBB</td>
      <td>CCC</td>
      <td>DDD</td>
    </tr>
  </tbody>
</table>