This is the bug that had me blaming CSS for a solid hour when CSS was never the problem. On an editorial site I built, the article card grid suddenly collapsed: each card shrank to a column about one character wide, and every word in the title dropped onto its own line, stacked vertically. The strange part was that the cover image inside the card still had a normal width. Only the title and byline were wrecked.
I poked at grid-template-columns, min-width, flex-basis, all of it. Nothing moved. The root cause turned out to be in the HTML, not the CSS: I had accidentally nested an <a> inside an <a>, and the browser quietly tore that structure apart.
Why a nested anchor destroys the grid
HTML5 forbids nested anchors. An <a> is interactive content, and the spec doesn't allow interactive content inside other interactive content. The catch is that the browser doesn't throw an error. It silently "repairs" your markup instead.
My card looked roughly like this: the whole card was wrapped in an <a> to make it clickable, and inside it sat another <a> for the author's name (a link to the author page). When the parser hit that second <a>, it implicitly closed the first <a> right at that point. As a result, everything after the author link (the title, the byline, the rest) was no longer inside the wrapping anchor. It got promoted to being a direct child of the grid element.
And that's where the grid collapses. A grid child with no explicit placement defaults to col-span-1. So a title that used to span the full card was now jammed into a single narrow column track, and its text broke apart word by word.
The fastest diagnostic to prove it: check the parent of the title element in DevTools.
// If the outer link got unwrapped, the h3's parent is no longer <a>
document.querySelector(".card h3").parentElement.tagName;
// Expected: "A" → Reality: "ARTICLE" (the wrapping anchor is gone)The moment that returns "ARTICLE" instead of "A", it's confirmed: the browser has unwrapped my outer anchor.
The broken markup
<a href="/article/slug" class="card">
<img src="/cover.webp" alt="" />
<h3>A long article headline</h3>
<!-- THIS is the culprit: an anchor inside an anchor -->
<a href="/author/jane">Jane Doe</a>
<time>June 9, 2026</time>
</a>The instant the parser reaches <a href="/author/jane">, it closes the .card anchor. The <h3>, the <time>, and the author link become direct siblings inside the grid, no longer children of the clickable card.
The fix: inner link as a <span> with data-href, not an <a>
We still need two distinct click targets: clicking the card goes to the article, clicking the name goes to the author page. The trick is to not use two anchors. Render the inner link as a non-interactive element that we give link behavior to by hand:
<a href="/article/slug" class="card">
<img src="/cover.webp" alt="" />
<h3>A long article headline</h3>
<!-- Not an <a>, so the outer anchor is never unwrapped -->
<span data-href="/author/jane" role="link" tabindex="0">Jane Doe</span>
<time>June 9, 2026</time>
</a>Because a <span> isn't interactive content, the browser no longer force-closes the outer anchor. The structure stays intact and the grid renders normally again. Then we wire up one delegated handler on the document to bring data-href to life:
// Click on a data-href element → navigate, and don't trigger the card link
document.addEventListener("click", (e) => {
const link = e.target.closest("[data-href]");
if (!link) return;
e.preventDefault();
e.stopPropagation(); // stop the card anchor from also firing
window.location.href = link.dataset.href;
});
// Keyboard: a span with role="link" must respond to Enter and Space
document.addEventListener("keydown", (e) => {
if (e.key !== "Enter" && e.key !== " ") return;
const link = e.target.closest("[data-href]");
if (!link) return;
e.preventDefault();
e.stopPropagation();
window.location.href = link.dataset.href;
});stopPropagation() matters: without it, clicking the author name would bubble up to the card anchor and take the user to the article instead of the author page. The role="link" and tabindex="0" restore the accessibility we lost by dropping the real <a>. The element becomes keyboard-focusable and is announced as a link by screen readers, and the keydown handler covers Enter/Space the way a real anchor would.
A few extra notes
- The fastest diagnostic still lives in DevTools: if an inner element's
parentElement.tagNameisn't what you expect, an interactive element was probably unwrapped by the browser. The same applies to a<button>inside an<a>, or an<a>inside a<button>. - The "big clickable card with one small link inside it" pattern is common. An alternative fix: make the card not an anchor (use an overlay pseudo-element
::afterwithposition: absolute; inset: 0), then keep the author link as a real<a>givenposition: relative; z-index: 1so it sits above the overlay. That preserves true anchor semantics for both. - Always validate the HTML when a layout breaks "for no reason." The validator flags it immediately as "Element a not allowed as child of element a", a far faster lead than spending an hour spelunking through CSS.
The takeaway: if a grid collapses and your CSS looks correct, check whether the browser has quietly rewritten your DOM. A nested anchor is one of the subtlest ways markup that "looks right" turns into a completely different structure once it's parsed.