3

When you use grid-areas the elements may change order. This will cause the visual :last-child to change. Is there a CSS selector to target the last column in a grid instead? Something like :last-grid-column.

The problem I am trying to solve. I like the columns to be separated by a border line. However I don't want the last column to have a border, because that wouldn't make visually sense. Now if I were to target it with :last-child it works until I change the order of the grid-template-areas to something like d b c a. Now the first column would not have a border because in the DOM it's still the last-child.

I understand you can have a work around where you would explicitly say these are the grid areas and I would omit the border for a in this case. But it would be so much more flexible if I could describe it semantically, then I could freely change the template areas without worrying that the border lines will be messed up.

.grid 
{
  display: grid;
  grid-template-areas: "d b c a";
  grid-template-columns: repeat( 4, 1fr );
}

.column.a { 
  grid-area: a; 
}

.column.b { 
  grid-area: b; 
}

.column.c { 
  grid-area: c; 
}

.column.d { 
  grid-area: d; 
}

.column:not(:last-child) {
  border-right: 1px solid black;
}

/* Flavor */
.grid 
{
  margin: 20px auto;
  max-width: 600px;
  padding: 20px;
  background: #F4F4F4;
}

.column { 
  width: 100%; 
  text-align: center;
}
<div class="grid">
  <div class="column a">A</div>
  <div class="column b">B</div>
  <div class="column c">C</div>
  <div class="column d">D</div>
</div>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
Mark
  • 16,906
  • 20
  • 84
  • 117

2 Answers2

3

Such selector exist but we are still far from having a good support for it. You can read:

To be able to represent such implied column-based relationships, the column combinator and the :nth-col() and :nth-last-col() pseudo-classes are defined

For your particular case, a background can do the job:

.grid 
{
  display: grid;
  grid-template-areas: "d b c a";
  grid-template-columns: repeat( 4, 1fr );
  background:
    linear-gradient(90deg,#0000 calc(100% - 1px), #000 0)
    0 50%/calc((100% + 1px)/4) 50% repeat-x
    #F4F4F4;
}

.column.a { 
  grid-area: a; 
}

.column.b { 
  grid-area: b; 
}

.column.c { 
  grid-area: c; 
}

.column.d { 
  grid-area: d; 
}


/* Flavor */
.grid 
{
  margin: 20px auto;
  max-width: 600px;
  padding: 20px;
}

.column { 
  width: 100%; 
  text-align: center;
}
<div class="grid">
  <div class="column a">A</div>
  <div class="column b">B</div>
  <div class="column c">C</div>
  <div class="column d">D</div>
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Good catch and the answer to the question. (https://css4-selectors.com/selector/css4/grid-structural-pseudo-class/) – Mark Aug 12 '21 at 11:28
1

One way to solve this problem is to show the border with each column and hide the border of the last column using a pseudo-element on the container element with the following CSS:

.grid::after {
   content: '';
   position: absolute;
   width: 20px;
   right: 0;
   height: 100%;
   background: #F4F4F4;
}

Trick here is to have the pseudo-element be of the same color as the container .grid element and its width to be equal to the padding of the .grid element.

To make this solution more maintainable, you could use CSS variables for padding and background-color of the .grid element.

Demo

.grid {
  display: grid;
  grid-template-areas: "d c b a";
  grid-template-columns: repeat( 4, 1fr);
  margin: 20px auto;
  max-width: 600px;
  padding: 20px;
  background: #F4F4F4;
  position: relative;
}

.column.a {
  grid-area: a;
}

.column.b {
  grid-area: b;
}

.column.c {
  grid-area: c;
}

.column.d {
  grid-area: d;
}

.column {
  width: 100%;
  text-align: center;
  border-right: 1px solid black;
}

.grid::after {
   content: '';
   position: absolute;
   width: 20px;
   right: 0;
   height: 100%;
   background: #F4F4F4;
}
<div class="grid">
  <div class="column a">A</div>
  <div class="column b">B</div>
  <div class="column c">C</div>
  <div class="column d">D</div>
</div>

Here's the previous demo with some Javascript to allow changing the grid-template-areas property dynamically.

const grid = document.querySelector(".grid");
const btn = document.querySelector("button");

btn.addEventListener("click", () => {
  grid.classList.toggle("change-column-order");
});
.grid {
  display: grid;
  grid-template-areas: "a b c d";
  grid-template-columns: repeat( 4, 1fr);
  margin: 20px auto;
  max-width: 600px;
  padding: 20px;
  background: #F4F4F4;
  position: relative;
}

.column.a {
  grid-area: a;
}

.column.b {
  grid-area: b;
}

.column.c {
  grid-area: c;
}

.column.d {
  grid-area: d;
}

.column {
  width: 100%;
  text-align: center;
  border-right: 1px solid black;
}

.grid::after {
   content: '';
   position: absolute;
   width: 20px;
   right: 0;
   height: 100%;
   background: #F4F4F4;
}

.change-column-order {
  grid-template-areas: "d c b a";
}
<div class="grid">
  <div class="column a">A</div>
  <div class="column b">B</div>
  <div class="column c">C</div>
  <div class="column d">D</div>
</div>

<button>Change Column Order</button>
Yousaf
  • 27,861
  • 6
  • 44
  • 69
  • So that would confirm there is no such css selector. Sneaky work around though, well done. – Mark Aug 12 '21 at 10:51
  • AFAIK, no. There is `:last-child` but, as you already know, it won't help in your case. – Yousaf Aug 12 '21 at 10:52