Using subgrid to avoid nested interactive elements
Nested interactive elements — like clickable things inside other clickable things — are a no-no on the web. Keyboard focus becomes a mess, screen readers have trouble announcing what’s what, and if you’re using semantic HTML, your HTML is probably invalid.
For a deeper dive on nested interactive elements, why they’re problematic, and approaches to mitigating them, Cat Johnson gave a nice talk on the topic.
Fortunately, sometimes a new(ish) CSS feature comes along that can help solve issues like this. In this case, subgrid. Let’s take an example. Here I have a menu of links, each of which may have an additional action button.

This may be a bit of a contrived example, but imagine that the entire gray box is the active link area, not just the text. Looking at it, you might be tempted to implement each item with this markup:
<li class="menu-item">
<a href="#">
<span>Link content</span>
<button class="action-button">More…</button>
</a>
</li>
When you do that, though, you end up with — dun dun dun — nested interactive elements.
Fixing the markup is simple: move the button to be outside the link.
<li class="menu-item">
<a href="#">Link content</a>
<button class="action-button">More…</button>
</li>
Excellent, now the interactive elements aren’t nested. But how do we make it look like the intended design?
To stack elements on top of each other, I usually like to use CSS grid, and this is no exception. To start, let’s make the li a grid container with two columns. The second column is auto, so its width is the width of the button; the first takes up the remaining space (1fr).
.menu-item {
display: grid;
grid-template-columns: 1fr auto;
}
Next, we want to place the <a> so it spans both columns, and put the button just in the second column. When placing items in a layout, I like to use nesting to make it clear that these declarations are specifically to place these items when also nested in the markup.
Remember that, in this format, grid-area is a shorthand for row-start / column-start / row-end / column-end and that negative numbers indicate counting explicit grid lines from the end, so nested under the .menu-item above, let’s add
& > a {
grid-area: 1 / 1 / -1 / -1;
}
& > .action-button {
grid-area: 1 / 2 / -1 / -1;
}
This means that both the link and button will be in the same row, the link will span both columns, and the button will start at column line 2.
At this point (with a bit of presentational styling), this gives us this. Perfect! Except for that little overlap problem….

Enter subgrid. Subgrid lets a grid item’s children participate in their grandparent’s grid. What that means is that by adding a wrapper around the link’s content, we can place the content in the first column even while keeping the whole link spanning both columns.
First, add the .item-content wrapper:
<li class="menu-item">
<a href="#">
<span class="item-content">Link content</span>
</a>
<button class="action-button">More…</button>
</li>
With the CSS we’ve already written, the link already spans both columns. What we’re going to do is make the <a> a grid container itself, and set grid-template-columns: subgrid. When we do that, all column line numbers (and column names, if there were any) are inherited from the link’s parent’s grid rather than created itself.
The link already spans both columns of the parent grid, and it now has two columns that line up exactly with those parent columns. It also only has one grid item, .item-content, so that automatically gets put in the first column, which is what we happen to want. We could leave it at that, but I like to be explicit just to make it clear where we want it.
So, nested under the & > a selector above, we’ll add this to place the item content in the first column:
& > .item-content {
grid-column: 1;
}

That’s it! Thanks to subgrid, the link text and button now appear next to each other, with the button looking like it’s inside the link even though it’s actually stacked on top of it.
The full result, including some extra presentational styles beyond the layout I just described, is in this Codepen:
See the Pen Subgrid for avoiding nested interative elements by Noah (@noleli) on CodePen.
Despite being a contrived example, it can also serve as a little reminder that it’s often preferable to start implementing a UI by writing good, accessible markup without too much consideration for the layout, then work to lay it out with CSS later, adding wrappers if necessary.