How to use CSS @container query with subgrid?

366 Views Asked by At

Let's consider example of cards with only subgrid feature.

body {
  max-width: 500px;
  margin: 1rem auto;
}

.container {
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-gap: 1rem;
}

.card {
  border: 1px solid #ccc;
  padding: 1rem;
  display: grid;
  grid-gap: 1rem;
  grid-row: span 3;
  grid-template-rows: subgrid;
}
<div class="container">
  <div class="card">
    <h2>Title Similique quisquam nesciunt</h2>
    <p>
      Body Lorem ipsum dolor sit, amet consectetur adipisicing elit. Similique quisquam nesciunt nihil totam laborum corporis animi expedita molestias, accusantium amet libero at neque id voluptatum, numquam, natus blanditiis eos sit!
    </p>
    <p>Footer nesciunt nihil</p>
  </div>
  <div class="card">
    <h2>Title accusantium amet libero at neque</h2>
    <p>
      Body Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quisquam minima ducimus numquam deleniti perspiciatis expedita nam commodi laboriosam illo! Blanditiis in dolorem eius. Hic assumenda architecto quidem magnam?
    </p>
    <p>Footer dolor sit amet consectetur adipisicing elit</p>
  </div>
</div>

This generates perfectly what I want (title, body and footer are align vertically):

enter image description here

Codesanbox for first implementation (https://codesandbox.io/s/silent-voice-cklrms), at time of 23.07.2023 I suggest to open it in Chrome Canary or Firefox.


Problem

When I try to add to my example CSS @container query I don't know how to keep the previous functionality of subgrid.

Consider this modification to HTML, addition of .card-outer-wrapper, reference why I had to add this wrapper:

body {
  max-width: 500px;
  margin: 1rem auto;
}

.container {
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-gap: 1rem;
}

.card {
  border: 1px solid #ccc;
  padding: 1rem;
  display: grid;
  grid-gap: 1rem;
  grid-row: span 3;
  grid-template-rows: subgrid;
}

.card-outer-wrapper {
  container-type: inline-size;
}

@container (min-width: 300px) {
  .card {
    background-color: aqua;
  }
}
<div class="container">
  <div class="card-outer-wrapper">
    <div class="card">
      <h2>Title Similique quisquam nesciunt</h2>
      <p>
        Body Lorem ipsum dolor sit, amet consectetur adipisicing elit. Similique quisquam nesciunt nihil totam laborum corporis animi expedita molestias, accusantium amet libero at neque id voluptatum, numquam, natus blanditiis eos sit!
      </p>
      <p>Footer nesciunt nihil</p>
    </div>
  </div>
  <div class="card-outer-wrapper">
    <div class="card">
      <h2>Title accusantium amet libero at neque</h2>
      <p>
        Body Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quisquam minima ducimus numquam deleniti perspiciatis expedita nam commodi laboriosam illo! Blanditiis in dolorem eius. Hic assumenda architecto quidem magnam?
      </p>
      <p>Footer dolor sit amet consectetur adipisicing elit</p>
    </div>
  </div>
</div>

It properly shows background color on wider card but it lacks of subgrid functionality:

enter image description here

Here is code sandbox for second example: https://codesandbox.io/s/nifty-feynman-n42s5p

Question

How to modify my second example to achieve subgrid alignment and @container query together?

Desired output should look like this: enter image description here

Highlight of desired vertical alignment: enter image description here

2

There are 2 best solutions below

6
On

It is my understanding that what you are trying to do is currently not possible.

If you set the value subgrid on grid-template-columns and/or grid-template-rows, the nested grid uses the tracks defined for its parent instead of creating a new track listing. This allows two sibling nested grids to have the same structure.

Container queries enable you to apply styles to an element based on the size of the element's container. In other words, you can apply styles to the descendent elements of the container, not the container itself.

However, when a wrapper div is added to be the container element for container queries, the wrapper div is the child of the grid container. The two card divs are no longer siblings and no longer share the same internal grid structure.

Additionally, I discovered through testing that the subgrid structure is lost if container-type is also declared on that element. Therefore, the following idea currently does not maintain subgrid structure:

.card {
  /* ... */
  grid-template-rows: subgrid;
  container-type: inline-size;
}

@container (min-width: 300px) {
  .card > * {
    background-color: aqua;
    margin: 0;
    padding: 1rem;
  }
}


I came up with a "hack" (workaround). If it's known what fraction of the grid container the column is, you can calculate how wide the grid container has to be in order for the column to be the targeted width. Therefore, you can get a result similar to the desired container query by applying the container query to the grid container and using pseudo-classes (such as :first-child, :nth-child()) to target individual columns.

Declare container-type for the grid container. Since you are using grid-template-columns: 2fr 1fr, the first column's width is 2/3 of the grid container (before factoring in padding and gap, which I won't address.) Therefore, you can calculate the min-width value of the container to be 3/2 times the targeted min-width of the first column. Finally, use the :first-child pseudo-class in the container query to target the first column.

(If every column is 1fr, then multiply the number of columns by the targeted min-width for each column and there's no need to use a pseudo-class.)

3 / 2 * 300px = 450px

.container {
  container-type: inline-size;
}

@container (min-width: 450px) {
  .card:first-child {
    background-color: aqua;
  }
}

body {
  max-width: 500px;
  margin: 1rem auto;
}

.container {
  container-type: inline-size;
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-gap: 1rem;
}

.card {
  border: 1px solid #ccc;
  padding: 1rem;
  display: grid;
  grid-gap: 1rem;
  grid-row: span 3;
  grid-template-rows: subgrid;
}

@container (min-width: 450px) {
  .card:first-child {
    background-color: aqua;
  }
}
<div class="container">
  <div class="card">
    <h2>Title Similique quisquam nesciunt</h2>
    <p>
      Body Lorem ipsum dolor sit, amet consectetur adipisicing elit. Similique quisquam nesciunt nihil totam laborum corporis animi expedita molestias, accusantium amet libero at neque id voluptatum, numquam, natus blanditiis eos sit!
    </p>
    <p>Footer nesciunt nihil</p>
  </div>
  <div class="card">
    <h2>Title accusantium amet libero at neque</h2>
    <p>
      Body Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quisquam minima ducimus numquam deleniti perspiciatis expedita nam commodi laboriosam illo! Blanditiis in dolorem eius. Hic assumenda architecto quidem magnam?
    </p>
    <p>Footer dolor sit amet consectetur adipisicing elit</p>
  </div>
</div>

2
On

According to your requirement, you have to calculate the width of the card and also want to change the background color of that card. But according to the official documentation of @container, we can't directly change the properties on which we apply container-type property. So, what I did is I wrapped every component inside the card with a div tag and used a background color to that div tag.

I got the answer like this

changed the background color of the first column

  body {
  max-width: 500px;
  margin: 1rem auto;
}
.container {
  display: grid;
  grid-template-columns: 2fr 1fr;
  grid-template-rows: auto;
  grid-gap:1rem;
}

.card {
  container-type: inline-size;
  border: 1px solid #ccc;
  display: grid;
  grid-row:span 3;
  grid-template-rows:subgrid;
}

.card div{
  padding:1rem;
}

@container (min-width: 300px) {
  .card div {
    background-color: aqua !important;
  }
}
<div class="container">
      <div class="card">
        <div>
        <h2>Title Similique quisquam nesciunt</h2>
        </div>
        <div>
        <p>
          Body Lorem ipsum dolor sit, amet consectetur adipisicing elit. Similique quisquam nesciunt nihil totam laborum corporis animi expedita molestias, accusantium amet libero at neque id voluptatum, numquam, natus blanditiis eos sit!
        </p>
       </div>
       <div>
        <p>Footer nesciunt nihil</p>
       </div>
      </div>
      <div class="card">
        <div>
        <h2>Title accusantium amet libero at neque</h2>
       </div>
       <div>
        <p>
          Body Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quisquam minima ducimus numquam deleniti perspiciatis expedita nam commodi laboriosam illo! Blanditiis in dolorem eius. Hic assumenda architecto quidem magnam?
        </p>
       </div>
       <div>
        <p>Footer dolor sit amet consectetur adipisicing elit</p>
       </div>
      </div>

  </div>

now I changed grid-templete-columns: 1fr 1fr 1fr

I got this as the result Result after changing the grid-templete-columns value to 1fr 1fr 1fr

body {
  max-width: 500px;
  margin: 1rem auto;
}

.container {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: auto;
  grid-gap:1rem;
}

.card {
  container-type: inline-size;
  border: 1px solid #ccc;
  display: grid;
  grid-row:span 3;
  grid-template-rows:subgrid;
}

.card div{
  padding:1rem;
}

@container (min-width: 300px) {
  .card div {
    background-color: aqua !important;
  }
}
<div class="container">
    <div class="card">
      <div>
      <h2>Title Similique quisquam nesciunt</h2>
      </div>
      <div>
      <p>
        Body Lorem ipsum dolor sit, amet consectetur adipisicing elit. Similique quisquam nesciunt nihil totam laborum corporis animi expedita molestias, accusantium amet libero at neque id voluptatum, numquam, natus blanditiis eos sit!
      </p>
     </div>
     <div>
      <p>Footer nesciunt nihil</p>
     </div>
    </div>
    <div class="card">
      <div>
      <h2>Title accusantium amet libero at neque</h2>
     </div>
     <div>
      <p>
        Body Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quisquam minima ducimus numquam deleniti perspiciatis expedita nam commodi laboriosam illo! Blanditiis in dolorem eius. Hic assumenda architecto quidem magnam?
      </p>
     </div>
     <div>
      <p>Footer dolor sit amet consectetur adipisicing elit</p>
     </div>
    </div>
    <div class="card">
     <div>
     <h2>Title accusantium amet libero at neque</h2>
     </div>
     <div>
      <p>
      Body Lorem, ipsum dolor sit amet consectetur adipisicing elit. Quisquam minima ducimus numquam deleniti perspiciatis expedita nam commodi laboriosam illo! Blanditiis in dolorem eius. Hic assumenda architecto quidem magnam?
      </p>
     </div>
     <div>
     <p>Footer dolor sit amet consectetur adipisicing elit</p>
     </div>
    </div>

</div>
</body>