使用 CSS Subgrid 的全新布局
New layouts with CSS Subgrid

原始链接: https://www.joshwcomeau.com/css/subgrid/

## CSS Subgrid:更高级的网格布局控制 CSS Subgrid 扩展了 CSS Grid 的功能,允许网格布局在 DOM 树中向下级联——这意味着父网格的结构可以与嵌套网格共享。虽然最初看起来只是方便,但 subgrid 释放了新的 UI 可能性,而不仅仅是简化现有的布局。 传统上,网格布局需要直接子元素,限制了语义化的 HTML 结构。Subgrid 解决了这个问题,使像列表 (`

    `) 这样的元素能够参与网格,从而提高可访问性和代码组织性。但是,使用 subgrid 需要在子元素上声明 `display: grid` 以继承父模板,并显式保留行空间以避免布局问题。 Subgrid 在需要跨嵌套元素对齐的场景中表现出色,例如定价表或复杂的卡片布局。虽然浏览器支持正在增长(但尚未达到 90%),但可以使用特性查询或替代布局来确保兼容性。 最终,subgrid 为 CSS 开发者提供了一个强大的新工具,能够实现更灵活和响应式设计,并允许在现有项目中逐步采用。像 Kevin Powell 的教程这样的资源对于掌握这个令人兴奋的新功能非常有价值。

    黑客新闻 新的 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 使用 CSS Subgrid 的全新布局 (joshwcomeau.com) 4 点赞 by soheilpro 1 小时前 | 隐藏 | 过去 | 收藏 | 1 条评论 gnabgib 1 小时前 [–] 你点赞了 Josh 的提交吗?https://news.ycombinator.com/item?id=46047053 回复 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请 YC | 联系 搜索:
    Joshwcomeau 分享了一篇新文章,展示了使用 CSS Subgrid 可以实现的创新布局。这篇文章在 Hacker News 的评论区引发了关于其效率和替代方案的讨论。 一位评论员指出,`display: contents` 在简单情况下可以实现类似的嵌套网格行为,而无需 Subgrid 的开销。 另一位建议使用固定单位来设置图像宽度,而不是分数单位 (`fr`),同时为文本使用 `fr`,从而提供另一种简化方法。 第三位评论员提出了 Container Queries 是否可能是一个更好的解决方案的问题,并承认 Subgrid 的优势在于保持整体网格的一致性。 此次讨论凸显了 CSS 布局工具的快速发展以及不同方法之间的权衡。
相关文章

原文
Introduction

When CSS Grid layout was first released, it came with a big asterisk: only the grid’s direct children could participate in the layout. “Subgrid” is a newer addition to CSS Grid which allows us to extend the grid layout down through the DOM tree.

When I first heard about subgrid, it seemed to me like a convenience, a way to make it a bit simpler to accomplish the same stuff I was already doing. As it turns out, subgrid is way more interesting than that. It opens whole new doors in terms of the UIs we can build!

In this tutorial, I’ll show you some of the exciting new things we can do with subgrid. Along the way, you’ll learn the basic mechanics of subgrid. We’ll even go over the most common gotchas!

We’ll get to the interesting stuff soon, but first, let’s start with the basics.

Suppose we want to implement the following mockup:

We can create this layout using a flat grid, no subgrid required. Here’s a quick implementation:

Code Playground

<style>
  .grid {
    display: grid;
    grid-template-columns: 35% 1fr 1fr 1fr;
    
    header {
      grid-row: 1 / 3;
    }
  }
</style>

<div class="grid">
  <header>
    <h1>My Portfolio</h1>
    <p>
      A small selection of the works created using Blender. No robots or AI involved.
    </p>
    <p>
      In a real artist portfolio, there would be more text here.
    </p>
  </header>
  <img alt="…" src="/img/thumb-sneakers.jpg" />
  <img alt="…" src="/img/thumb-rocket.jpg" />
  <img alt="…" src="/img/thumb-fish.jpg" />
  <img alt="…" src="/img/thumb-guitar-pedals.jpg" />
  <img alt="…" src="/img/thumb-machine.jpg" />
  <img alt="…" src="/img/thumb-particles.jpg" />
</div>

If we check the “Grid” devtools, we see that this is a 4x2 grid, with the header spanning the first two rows:

In order for this to work without subgrid, every grid participant has to be a direct child of the .grid container. Sure enough, if we inspect the HTML, we see the following structure:

<div class="grid">
  <header>
    <h1></h1>
    <p></p>
  </header>
  <img alt="" src="/img/thumb-sneakers.jpg" />
  <img alt="" src="/img/thumb-rocket.jpg" />
  <img alt="" src="/img/thumb-fish.jpg" />
  <img alt="" src="/img/thumb-guitar-pedals.jpg" />
  <img alt="" src="/img/thumb-machine.jpg" />
  <img alt="" src="/img/thumb-particles.jpg" />
</div>

Semantically, this feels a bit funky to me. I feel like these images should be grouped in a list, since we’re displaying a collection of portfolio pieces. Proper semantic markup will provide more context to folks using assistive technologies like screen readers, and to search engines that are trying to make sense of our page.

Unfortunately, adding this extra markup throws a wrench into the grid:

Code Playground

<div class="grid">
  <header>
    <h1>My Portfolio</h1>
    <p>
      A small selection of the works created using Blender. No robots or AI involved.
    </p>
    <p>
      In a real artist portfolio, there would be more text here.
    </p>
  </header>
  
  
  <ul>
    <li><img alt="…" src="/img/thumb-sneakers.jpg" /></li>
    <li><img alt="…" src="/img/thumb-rocket.jpg" /></li>
    <li><img alt="…" src="/img/thumb-fish.jpg" /></li>
    <li><img alt="…" src="/img/thumb-guitar-pedals.jpg" /></li>
    <li><img alt="…" src="/img/thumb-machine.jpg" /></li>
    <li><img alt="…" src="/img/thumb-particles.jpg" /></li>
  </ul>
</div>

Instead of having each image occupy its own grid cell, we instead cram the entire list of images into a single cell in the second column, leaving the final two columns totally empty. 😬

CSS subgrid allows us to extend the parent grid through that <ul> tag, so that the images can participate in the main grid. Here’s what that looks like:

Code Playground

<style>
  .grid {
    display: grid;
    grid-template-columns: 35% 1fr 1fr 1fr;
  }
  .grid header {
    grid-row: 1 / 3;
  }
  
  .grid ul {
    grid-row: span 2;
    grid-column: span 3;
    display: grid;
    grid-template-rows: subgrid;
    grid-template-columns: subgrid;
  }
</style>

<div class="grid">
  <header>
    <h1>My Portfolio</h1>
    <p>
      A small selection of the works created using Blender. No robots or AI involved.
    </p>
    <p>
      In a real artist portfolio, there would be more text here.
    </p>
  </header>
  
  <ul>
    <li><img alt="…" src="/img/thumb-sneakers.jpg" /></li>
    <li><img alt="…" src="/img/thumb-rocket.jpg" /></li>
    <li><img alt="…" src="/img/thumb-fish.jpg" /></li>
    <li><img alt="…" src="/img/thumb-guitar-pedals.jpg" /></li>
    <li><img alt="…" src="/img/thumb-machine.jpg" /></li>
    <li><img alt="…" src="/img/thumb-particles.jpg" /></li>
  </ul>
</div>

There’s a lot going on here, so let’s unpack it.

  1. Using grid-column and grid-row, we assign the <ul> to span three columns and two rows. This is how we specify which portion of the grid we want to share with the <ul>’s descendants. We’ll dig more into this later.

  2. Next, we apply display: grid to the <ul>, to create a new child grid.

  3. Finally, we pass along the row/column definitions using grid-template-rows and grid-template-columns. The subgrid keyword is the key bit of magic that ties the two grids together, allowing each <li> to occupy its own cell in the parent grid.

When I first learned about subgrid, this is the sort of scenario I was imagining: cases where nested HTML elements like <ul> + <li> or <figure> + <figcaption> block us from assigning the actual UI elements to the grid. CSS subgrid is a nifty lil’ escape hatch for these types of situations!

That said, it's not like we haven’t had other ways to solve these kinds of problems. Instead of sharing a single CSS grid template with subgrid, we could instead combine a Flexbox row with a nested grid:

Code Playground

<style>
  
  .wrapper {
    display: flex;
    
    
    header {
      flex-basis: 35%;
    }
    
    
    .grid {
      flex: 1;
      display: grid;
      grid-template-columns: 1fr 1fr 1fr;
    }
  }
</style>

<div class="wrapper">
  <header>
    <h1>My Portfolio</h1>
    <p>
      A small selection of the works created using Blender. No robots or AI involved.
    </p>
    <p>
      In a real artist portfolio, there would be more text here.
    </p>
  </header>
  
  <ul class="grid">
    <img src="/img/thumb-sneakers.jpg" />
    <img src="/img/thumb-rocket.jpg" />
    <img src="/img/thumb-fish.jpg" />
    <img src="/img/thumb-guitar-pedals.jpg" />
    <img src="/img/thumb-machine.jpg" />
    <img src="/img/thumb-particles.jpg" />
  </ul>
</div>

Instead of trying to rig everything up to use a single grid structure, we can often create the same layout with nested combinations of Flexbox/Grid. And honestly, I think I prefer this approach in this case! It feels simpler to me.

But like I said earlier, this isn’t the most exciting use case for subgrid. Now that we’ve covered the basic syntax, we can explore some of the more interesting possibilities. 😄

Sticking with the artist portfolio example, let’s suppose we have this card design:

A big yellow pufferfish

Bret’s Dead Fish

I created this render for the Animation Design module in my upcoming course, Whimsical Animations(opens in new tab). The fish is a nod to Bret Victor’s talk, “Stop Drawing Dead Fish”, which is referenced in the course.

This looks alright on its own, but something funky happens when we put it in a grid:

Code Playground

<style>
  .grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    
    @media (max-width: 32rem) {
      grid-template-columns: 1fr;
    }
  }
  .grid article {
    display: grid;
    grid-template-columns: 2fr 1fr;
  }
</style>

<div class="grid">
  <article>
    <img
      alt="A big yellow pufferfish"
      src="/img/thumb-fish.jpg"
    />
    <div class="content">
      <h2>Bret’s Dead Fish</h2>
      <p>
        I created this render for the Animation Design module in my
        upcoming course,
        <a href="https://whimsy.joshwcomeau.com/" target="_blank"
          >Whimsical Animations</a
        >. The fish is a nod to Bret Victor’s talk, “Stop Drawing Dead
        Fish”, which is referenced in the course.
      </p>
    </div>
  </article>
  <article>
    <img
      alt="two white sneakers with pink details and a shiny sparkly rainbow"
      src="/img/thumb-sneakers.jpg"
    />
    <div class="content">
      <h2>Big Shoes To Fill</h2>
      <p>
        In this piece, I tried to create my own sneaker design, taking
        inspiration from the Air Force Ones I’ve been wearing for most of
        my adult life. Topographically, shoes are a really weird shape, so
        this was a good challenge!
      </p>
    </div>
  </article>
  <article>
    <img
      alt="three colorful guitar pedals, with foot controls and knobs"
      src="/img/thumb-guitar-pedals.jpg"
    />
    <div class="content">
      <h2>Guitar Pedalboard</h2>
      <p>
        Over the past few years, I’ve been getting back into music
        production, and have started collecting effect pedals. This render
        is my attempt to create my own pedal designs. The middle one is
        meant to look a bit like Zoidberg.
      </p>
    </div>
  </article>
  <article>
    <img
      alt="A very complicated machine with a plane-style throttle, a piano keyboard, radar, a bunch of sliders and knobs, and so much more"
      src="/img/thumb-machine.jpg"
    />
    <div class="content">
      <h2>Infinite Supercomputer</h2>
      <p>
        I spent more time than I’d care to admit creating an enormous
        machine in Blender, full of weird knobs and sliders and extras. The
        goal was to produce a completely ridiculous cockpit-style panel.
      </p>
    </div>
  </article>
</div>

Notice that the images are different widths? The fish image, for example, is much wider than the final supercomputer image. What’s going on here? 🤔

Well, let’s take a look at the CSS. The four cards are arranged in a two-column grid (which shrinks to a one-column grid on smaller screens):

.grid {
  display: grid;
  grid-template-columns: 1fr 1fr;

  @media (max-width: 32rem) {
    grid-template-columns: 1fr;
  }
}

We’re populating this top-level grid with four <article> cards. Each card declares its own two-column grid:

.grid article {
  display: grid;
  grid-template-columns: 2fr 1fr;
}

The goal here is for the image to take up the lion’s share of the space within each card, since that’s the important part (the point of an artist’s portfolio, after all, is to showcase the art!). But the fr unit is designed to be flexible; it will try to match the requested ratio, but it’ll adapt based on the content.

This is actually a very good thing. We could force the image column to be a fixed size, but we wouldn’t like the results:

Code Playground

<style>
  .grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    
    @media (max-width: 32rem) {
      grid-template-columns: 1fr;
    }
  }
  .grid article {
    display: grid;
    
    
    grid-template-columns: 66% 1fr;
  }
</style>

<div class="grid">
  <article>
    <img
      alt="A big yellow pufferfish"
      src="/img/thumb-fish.jpg"
    />
    <div class="content">
      <h2>Bret’s Dead Fish</h2>
      <p>
        I created this render for the Animation Design module in my
        upcoming course,
        <a href="https://whimsy.joshwcomeau.com/" target="_blank"
          >Whimsical Animations</a
        >. The fish is a nod to Bret Victor’s talk, “Stop Drawing Dead
        Fish”, which is referenced in the course.
      </p>
    </div>
  </article>
  <article>
    <img
      alt="two white sneakers with pink details and a shiny sparkly rainbow"
      src="/img/thumb-sneakers.jpg"
    />
    <div class="content">
      <h2>Big Shoes To Fill</h2>
      <p>
        In this piece, I tried to create my own sneaker design, taking
        inspiration from the Air Force Ones I’ve been wearing for most of
        my adult life. Topographically, shoes are a really weird shape, so
        this was a good challenge!
      </p>
    </div>
  </article>
  <article>
    <img
      alt="three colorful guitar pedals, with foot controls and knobs"
      src="/img/thumb-guitar-pedals.jpg"
    />
    <div class="content">
      <h2>Guitar Pedalboard</h2>
      <p>
        Over the past few years, I’ve been getting back into music
        production, and have started collecting effect pedals. This render
        is my attempt to create my own pedal designs. The middle one is
        meant to look a bit like Zoidberg.
      </p>
    </div>
  </article>
  <article>
    <img
      alt="A very complicated machine with a plane-style throttle, a piano keyboard, radar, a bunch of sliders and knobs, and so much more"
      src="/img/thumb-machine.jpg"
    />
    <div class="content">
      <h2>Infinite Supercomputer</h2>
      <p>
        I spent more time than I’d care to admit creating an enormous
        machine in Blender, full of weird knobs and sliders and extras. The
        goal was to produce a completely ridiculous cockpit-style panel.
      </p>
    </div>
  </article>
</div>

On certain viewport sizes, the cards simply aren’t large enough to devote ⅔rds of the available space to the image and still contain the text content. If we force that column to have a fixed size, the text could wind up overflowing:

So, the flexibility we get from the fr unit is a good thing. The problem is that each card is doing its own internal calculation. The heading in the first card (“Bret’s Dead Fish”) is made up of small words, so it can fit comfortably in a narrow column. But the final card’s heading (“Infinite Supercomputer”) requires quite a bit more room.

If you’ve worked with CSS for a while, you’ve probably gotten stuck in cul-de-sacs like this. One of the hardest problems in CSS is when siblings need to be aware of each other inside nested / complex layouts.

Miraculously, subgrid offers a solution to these sorts of problems. Check this out:

Code Playground

<style>
  .grid {
    display: grid;
    grid-template-columns: repeat(2, 2fr 1fr);
    
    @media (max-width: 32rem) {
      grid-template-columns: 2fr 1fr;
    }
  }
  .grid article {
    grid-column: span 2;
    display: grid;
    grid-template-columns: subgrid;
  }
</style>

<div class="grid">
  <article>
    <img
      alt="A big yellow pufferfish"
      src="/img/thumb-fish.jpg"
    />
    <div class="content">
      <h2>Bret’s Dead Fish</h2>
      <p>
        I created this render for the Animation Design module in my
        upcoming course,
        <a href="https://whimsy.joshwcomeau.com/" target="_blank"
          >Whimsical Animations</a
        >. The fish is a nod to Bret Victor’s talk, “Stop Drawing Dead
        Fish”, which is referenced in the course.
      </p>
    </div>
  </article>
  <article>
    <img
      alt="two white sneakers with pink details and a shiny sparkly rainbow"
      src="/img/thumb-sneakers.jpg"
    />
    <div class="content">
      <h2>Big Shoes To Fill</h2>
      <p>
        In this piece, I tried to create my own sneaker design, taking
        inspiration from the Air Force Ones I’ve been wearing for most of
        my adult life. Topographically, shoes are a really weird shape, so
        this was a good challenge!
      </p>
    </div>
  </article>
  <article>
    <img
      alt="three colorful guitar pedals, with foot controls and knobs"
      src="/img/thumb-guitar-pedals.jpg"
    />
    <div class="content">
      <h2>Guitar Pedalboard</h2>
      <p>
        Over the past few years, I’ve been getting back into music
        production, and have started collecting effect pedals. This render
        is my attempt to create my own pedal designs. The middle one is
        meant to look a bit like Zoidberg.
      </p>
    </div>
  </article>
  <article>
    <img
      alt="A very complicated machine with a plane-style throttle, a piano keyboard, radar, a bunch of sliders and knobs, and so much more"
      src="/img/thumb-machine.jpg"
    />
    <div class="content">
      <h2>Infinite Supercomputer</h2>
      <p>
        I spent more time than I’d care to admit creating an enormous
        machine in Blender, full of weird knobs and sliders and extras. The
        goal was to produce a completely ridiculous cockpit-style panel.
      </p>
    </div>
  </article>
</div>

How cool is this?? 🤯

In the original version, the parent grid was a one-column layout (on smaller screens), and it contained a bunch of independent grids. In this new version, the parent grid holds the two-column layout:

In the original version, the parent grid was a two-column layout, with each card assigned to a grid cell. In this new version, the parent grid grows to four columns:

Each <article> will span two of these columns (grid-column: span 2), and inherits the column definitions from the parent (grid-template-column: subgrid).

As a result, the grid can dynamically react to content changes. Try erasing the word “Supercomputer” in the playground above and notice how the columns readjust!

As a result, the grid can dynamically react to content changes. If that final card (“Infinite Supercomputer”) had a shorter title, the whole grid would rearrange, shrinking the text columns and allowing more of the images to be shown.

Honestly, I’m not really used to thinking about layouts like this. Before subgrid, I would’ve solved this problem by picking a very narrow fixed width for the image column, so that there was always enough space for the text column. This would ensure that the layout never breaks, but remember, the goal of a portfolio is to display as much of the images as possible! Subgrid allows us to adapt to the content dynamically, so that we can produce the best possible UI in various contexts.

This is where subgrid truly shines, in my opinion. By extending the grid downwards, it means that we can allow siblings to become responsive to each other, in a way that hasn’t been possible until now. ✨

As I’ve been experimenting with subgrid, there have been a couple of things that have caught me off guard. Let’s go over them, so that you’re well-prepared!

Link to this headingReserving space for the subgrid

Sharing columns with subgrid tends to be pretty intuitive, but things get a bit more quirky when sharing rows.

To help me explain, let’s look at a different example. Suppose our design team wants us to build the following pricing UI, to show the features included at different price tiers:

This seems like a pretty straightforward task, but the devil is in the details. If we use a typical Grid or Flexbox strategy, we’ll wind up with asymmetrical rows:

This might look right at a quick glance, but notice how the features don’t line up. In the original mockup, the first line of every feature is perfectly aligned with the same feature in the opposite card!

Historically, the only way to achieve this sort of thing in CSS has been with Table layout (using <table> tags, or display: table). It’s not really practical to use a table here, though, since we’d need each card to be its own column in the same table, and we can’t easily style table columns.

Subgrid to the rescue! At least in theory, we should be able to let both cards share a single grid, like this:

Unfortunately, there’s a very easy mistake to make. See if you can spot the problem with this code:

Code Playground

<style>
  .grid {
    display: grid;
    grid-template-columns: 1fr 1fr;

    .card, .card ul {
      display: grid;
      grid-template-rows: subgrid;
    }
  }
</style>

<div class="grid">
  <div class="card">
    <h2>Pro Package</h2>
    <ul>
      <li>Up to 4 team accounts.</li>
      <li>Basic workflows.</li>
      <li>Connect with Slack™.</li>
      <li>Up to 3 knowledge bases, with 100gb total storage.</li>
      <li>Limited AI assistant (depending on region and language).</li>
    </ul>
  </div>
  <div class="card">
    <h2>Enterprise Package</h2>
    <ul>
      <li>Unlimited team accounts.</li>
      <li>Advanced, fully-customizeable workflows.</li>
      <li>Connect with Slack™, Microsoft Teams™, Discord™, and 5 other popular integrations.</li>
      <li>Unlimited knowledge bases.</li>
      <li>Unlimited robots. 🤖</li>
    </ul>
  </div>
</div>

All of the text is clumped up in the same spot! If we inspect this using the Grid devtools, we discover that we’ve wound up with a 2×1 grid. All of the content within each card is smushed into a single row. 😬

Typically, with CSS Grid, we don’t need to explicitly define any rows. I usually define the number of columns, and trust the grid algorithm to add new rows as-needed, so that each child gets its own grid cell.

Unfortunately, with subgrid, it doesn't quite work like this. By default, our child grid will only span a single grid column/row. If we want it to occupy multiple rows, we need to reserve them explicitly.

Here’s what the fix looks like:

Code Playground

<style>
  .grid {
    display: grid;
    grid-template-columns: 1fr 1fr;

    .card {
      
      grid-row: span 6;
      display: grid;
      grid-template-rows: subgrid;
    }
    .card ul {
      
      grid-row: span 5;
      display: grid;
      grid-template-rows: subgrid;
    }
  }
</style>

<div class="grid">
  <div class="card">
    <h2>Pro Package</h2>
    <ul>
      <li>Up to 4 team accounts.</li>
      <li>Basic workflows.</li>
      <li>Connect with Slack™.</li>
      <li>Up to 3 knowledge bases, with 100gb total storage.</li>
      <li>Limited AI assistant (depending on region and language).</li>
    </ul>
  </div>
  <div class="card">
    <h2>Enterprise Package</h2>
    <ul>
      <li>Unlimited team accounts.</li>
      <li>Advanced, fully-customizeable workflows.</li>
      <li>Connect with Slack™, Microsoft Teams™, Discord™, and 5 other popular integrations.</li>
      <li>Unlimited knowledge bases.</li>
      <li>Unlimited robots. 🤖</li>
    </ul>
  </div>
</div>

The extra-complicated thing about this setup is that we’re extending the grid down two layers:

  • First, we extend it to <div class="card">, which includes an <h2> and a <ul>.

  • Next, we extend it to that child <ul>, so that the individual list items each get their own row.

There are 5 list items in this case, which means we need 6 rows total (one for the heading, five for the list). If we don’t “reserve” all of these rows explicitly, then the browser will shove everything into a single row and make a big mess, like we saw above. I’m not exactly sure why the typical auto-assignment algorithm doesn’t work with subgrid, but I assume there’s some technical limitation.

This is mind-bending stuff, but it becomes intuitive with a bit of practice. The thing to keep in mind is that subgrids, by default, will only occupy a single grid cell. In order to spread a group of items across multiple grid rows, the subgrid must first stretch across that area itself.

Link to this headingNested grid numbers

We got the gnarliest gotcha out of the way first! I promise the next two won’t be as intellectually taxing. 😅

In CSS grid, the lines between each column are numbered, and we can assign grid children using these numbers. This is something we explore in greater depth in “An Interactive Guide to CSS Grid”:

When we inherit a portion of the grid using grid-template-rows: subgrid or grid-template-columns: subgrid, the line numbers get reset.

Here’s an example of what I’m talking about:

Code Playground

<style>
  .grid {
    display: grid;
    grid-template-columns:
      repeat(4, 1fr);
    grid-template-rows:
      repeat(4, 1fr);

    .subgrid {
      grid-column: 2 / 5;
      grid-row: 2 / 5;
      display: grid;
      grid-template-columns: subgrid;
      grid-template-rows: subgrid;

      .child {
        grid-column: 2;
        grid-row: 2;
      }
    }
  }
</style>

<div class="grid">
  <div class="subgrid">
    <div class="child"></div>
  </div>
</div>

Our yellow .child is assigned to grid-column: 2 and grid-row: 2, but it winds up sitting in the third of the grid’s four rows and columns. 🤔

It turns out that while the grid template is inherited with subgrid, the line indexes don’t. Our .subgrid grid inherits columns/rows 2 through 4, but internally, they get re-indexed as 1 through 3.

We can see this using the grid devtools in the Elements inspector:

In my mind, I had been thinking of line numbers as unique IDs, and so I figured that if the subgrid is inheriting the grid template, those IDs would come along for the ride too. But if we think of these line numbers as indices rather than IDs, this behaviour makes a lot more sense. In every grid, the first line has index 1, even if that row/column is inherited from a parent grid.

Link to this headingIncompatibility with fluid grids

Perhaps the most famous grid snippet is this lil’ guy:

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
}

This is a fluid design concept. Instead of specifying different grid templates at different viewport sizes using media queries, we specify that we want as many columns as possible, as long as they’re all at least 100px wide (or whatever the minimum specified size is).

Try resizing the “Result” pane by dragging the vertical divider, and notice how the columns adjust:

Code Playground

<style>
  .grid {
    display: grid;
    grid-template-columns:
      repeat(
        auto-fill,
        minmax(100px, 1fr)
      );
  }
</style>

<div class="grid">
  <div class="child">A</div>
  <div class="child">B</div>
  <div class="child">C</div>
  <div class="child">D</div>
  <div class="child">E</div>
  <div class="child">F</div>
</div>

This is a very cool approach, but unfortunately, it doesn’t quite work with some of the new UI possibilities introduced by subgrid. For example, the “portfolio card” grid we explored earlier requires that we list the specific number of columns. We can’t use auto-fill or auto-fit.

(Or, more accurately, I haven’t found a way to use fluid design in conjunction with that subgrid pattern. If you’ve found a solution, please let me know on Bluesky!(opens in new tab))

Link to this headingSupporting older browsers

Subgrid has been supported across all major browsers since 2023. Surprisingly, though, subgrid support still hasn’t hit 90% yet (according to caniuse(opens in new tab), as of November 2025).

This presents a bit of a challenge. As we’ve seen in this blog post, subgrid enables us to solve problems that were previously unsolvable. What should we do for folks who visit using older browsers?

Well, we can’t produce an identical experience, but I think with a bit of creative problem-solving, we can come up with alternative layouts that are good enough. Using the artist portfolio example from earlier, we could reconfigure the card layout so that the image is stacked vertically, rather than horizontally:

We can accomplish this using feature queries. Here’s what the code looks like:

@supports not (grid-template-columns: subgrid) {
  .grid article {
    grid-template-columns: 1fr;
    grid-template-rows: 140px 1fr;
  }
}

Alternatively, I could have kept the two-column layout but restricted the image column’s width (eg. grid-template-columns: 50px 1fr). This would’ve preserved the original design for everyone. But I think when it comes to fallbacks, the goal isn't to be as similar to the original as possible, the goal is to produce the best experience possible. In this particular case, I think a single-column fallback experience works better.

I’m publishing this post on November 25th, a frankly miserable time of year up here in the northern hemisphere 😅. The days are getting shorter, the weather is getting colder, and my favourite season (autumn) is transmogrifying into my least favourite season (winter).

But there is one silver lining about this time of year: everything’s on sale for Black Friday! 🎈

For the past few years, my main focus has been creating comprehensive interactive online courses. I have two flagship courses, CSS for JavaScript Developers(opens in new tab) and The Joy of React(opens in new tab), and this week, they’re up to 50% off(opens in new tab)!

If you found this blog post useful, you’ll likely get so much out of my CSS course. We focus on understanding CSS at a deeper level, building an intuition for how the language actually works. No more memorizing snippets, or trying random stuff hoping that the UI will snap into the right shape!

I know that in the world of e-commerce, things go on sale every other week. That’s not how I roll, though. I only have one or two sales a year. So this truly is a rare chance to pick up one of my courses for a deep discount. ✨

You can learn more here:

One of the coolest websites I’ve seen in a while is Stripe’s developer site(opens in new tab).

If we pop open the grid devtools, we see that the entire layout is one big grid, passed down through several layers of subgrids:

This is incredibly cool, and I think it’s a great demonstration of the maximalist things we can do with subgrid. But, honestly, I think I’m more excited by the smaller-scale stuff we’ve seen in this blog post. 😅

Subgrid is a very versatile new tool, and it can be a bit intimidating and overwhelming, but hopefully this post has given you some ideas for the sorts of things you can start experimenting with. The good news is that you don’t have to re-architect your entire project in order to start using subgrid! The most powerful parts of subgrid are things which can be incrementally adopted.

Another special thanks to Kevin Powell. The examples in this blog post would’ve been far less compelling without his inspiration. 😄

Last updated on

November 25th, 2025

联系我们 contact @ memedata.com