How to make hover state on row table with CSS grid layout

Here is a trick using pseudo-element. The idea is to use the element as a background and make it overflow on the left and right so that it will cover all the row. Like that if you hover on any element inside a row you change the color and it's like you changed the color of the whole row.

This trick involve few changes on the markup and also more CSS.

.table {
  display: grid;
  grid-template-columns: [col-start] auto [col-end];
  grid-template-rows: [header-start] 50px [header-end row-start] auto [row-end];
  grid-auto-rows: auto;
  grid-auto-columns: auto;
  grid-gap: 1px;
  overflow: hidden;
  background: gray;
}

.table>* {
  padding: 10px;
  position: relative;
}

.heading {
  background: navy;
  color: #fff;
  grid-row: header;
}

.row span {
  position: relative;
  z-index: 2;
}

.row:before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: -1000%;
  left: -1000%;
  z-index: 1;
}

.row:after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: -1px;
  width: 1px;
  z-index: 2;
  background-color: #fff;
}

.row:nth-child(5n+5)::after {
  bottom: -1px;
  right: 0;
  left: -1000%;
  height: 1px;
  z-index: 3;
  width: auto;
  top: auto;
  background-color: #fff;
}

.row:hover::before {
  background-color: red;
}
<div class="table">
  <div class="heading">Title 1</div>
  <div class="heading">Title 2</div>
  <div class="heading">Title 3</div>
  <div class="heading">Title 4</div>
  <div class="heading">Title 5</div>

  <div class="row"><span>Row 1</span></div>
  <div class="row"><span>Row 1</span></div>
  <div class="row"><span>Row 1</span></div>
  <div class="row"><span>Row 1</span></div>
  <div class="row"><span>Row 1</span></div>

  <div class="row"><span>Row 2</span></div>
  <div class="row"><span>Row 2</span></div>
  <div class="row"><span>Row 2</span></div>
  <div class="row"><span>Row 2</span></div>
  <div class="row"><span>Row 2</span></div>

  <div class="row"><span>Row 3</span></div>
  <div class="row"><span>Row 3</span></div>
  <div class="row"><span>Row 3</span></div>
  <div class="row"><span>Row 3</span></div>
  <div class="row"><span>Row 3</span></div>
</div>

Here's my solution, based on sibling combinators.
The main part is:

.datacell:hover ~ .datarow {
    background-color: rgba(255,255,0,0.5);
}
.datacell:hover ~ .datarow + .datacell ~ .datarow{
    background-color: transparent;
}

Snippet:

.datatable{
    display:grid;
    grid-gap: 0;
    grid-template-columns: auto 1fr auto;
    position: relative;
}

.datarow{
    grid-column: 1 / 4;
    z-index: 0;
}

.datacell{
    z-index: 1;
    padding: 8px;
    border-bottom: 1px solid #c0c0c0;
}

.datacell:hover ~ .datarow {
    background-color: rgba(255,255,0,0.5);
}
.datacell:hover ~ .datarow + .datacell ~ .datarow{
    background-color: transparent;
}

.row-1{ grid-row: 1;}
.row-2{ grid-row: 2;}
.row-3{ grid-row: 3;}

.col-1{ grid-column: 1;}
.col-2{ grid-column: 2;}
.col-3{ grid-column: 3;}
<div class="datatable">
   <div class="datacell col-1 row-1">Row 1 Column 1</div>
   <div class="datacell col-2 row-1">Row 1 Column 2</div>
   <div class="datacell col-3 row-1">Row 1 Column 3</div>       
   <div class="datarow row-1"></div>

   <div class="datacell col-1 row-2">Row 2 Column 1</div>
   <div class="datacell col-2 row-2">Row 2 Column 2</div>
   <div class="datacell col-3 row-2">Row 2 Column 3</div>       
   <div class="datarow row-2"></div>

   <div class="datacell col-1 row-3">Row 3 Column 1</div>
   <div class="datacell col-2 row-3">Row 3 Column 2</div>
   <div class="datacell col-3 row-3">Row 3 Column 3</div>       
   <div class="datarow row-3"></div>

</div>

The html must be structured so that a .datarow element closes the virtual grid row and spans all the preceding cells. Explicitation of the positioning inside the grid is needed in order to let cells and rows overlap.


Since you are treating this as a table, where the elements stay in the same row, you can also wrap each row in a DIV with "display" set to "contents." This forms an innocuous parent element that you use for hover, and then style the child elements. (display: contents is not yet supported in Edge, though.)

.table {
  display: grid;
  grid-template-columns: [col-start] auto [col-end];
  grid-template-rows: [header-start] 50px [header-end row-start] auto [row-end];
  grid-auto-rows: auto;
  grid-auto-columns: auto;
  grid-gap: 1px;
  overflow: hidden;
  background: gray;
}

.table .row, .table .heading{
  padding: 10px;
  position: relative;
}

.heading {
  background: navy;
  color: #fff;
  grid-row: header;
}

.row span {
  position: relative;
  z-index: 2;
}

.rowWrapper{
  display: contents;
}

.rowWrapper:hover > div{
  background-color: orange;
}
<div class="table">

        <div class="heading">Title 1</div>
        <div class="heading">Title 2</div>
        <div class="heading">Title 3</div>
        <div class="heading">Title 4</div>
        <div class="heading">Title 5</div>
      
    <div class="rowWrapper">
        <div class="row"><span>Row 1</span></div>
        <div class="row"><span>Row 1</span></div>
        <div class="row"><span>Row 1</span></div>
        <div class="row"><span>Row 1</span></div>
        <div class="row"><span>Row 1</span></div>
    </div>

    <div class="rowWrapper">
        <div class="row"><span>Row 2</span></div>
        <div class="row"><span>Row 2</span></div>
        <div class="row"><span>Row 2</span></div>
        <div class="row"><span>Row 2</span></div>
        <div class="row"><span>Row 2</span></div>
    </div>
      
    <div class="rowWrapper">
        <div class="row"><span>Row 3</span></div>
        <div class="row"><span>Row 3</span></div>
        <div class="row"><span>Row 3</span></div>
        <div class="row"><span>Row 3</span></div>
        <div class="row"><span>Row 3</span></div>
    </div>
    
</div>

I ended up with a different solution. My table looked like this:

.table {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
<div class="table">
  <span>head1</span>
  <span>head2</span>
  <span>head3</span>
  <span>row1 col1</span>
  <span>row1 col2</span>
  <span>row1 col3</span>
  <span>row2 col1</span>
  <span>row2 col2</span>
  <span>row2 col3</span>
</div>

Now you want the hover state per row. Adding an element wrapping each row breaks the parent grid as it would really be the children of those row wrappers you're trying to lay out in a grid. In addition, I preferred:

  • Not using a <table>. Which about doubled the number of lines needed and required more styling.
  • Not using nth-child puzzling.
  • One decent option I didn't try seems to be wrapping each row in a div, add a class .row and display: contents, then with .row:hover > span as a selector you could add opacity: 0.6. Seems decent! Some other answers go in this direction.

I ended up going for the following which I feel is simpler:

.row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.row:hover {
  opacity: 0.6
}
<div class="row">
  <span>head1</span>
  <span>head2</span>
  <span>head3</span>
</div>
<div class="row">
  <span>row1 col1</span>
  <span>row1 col2</span>
  <span>row1 col3</span>
</div>
<div class="row">
  <span>row2 col1</span>
  <span>row2 col2</span>
  <span>row2 col3</span>
</div>