Entry 05
Whole card clickable from one link
Use it for cards, list rows and tiles where the whole area should navigate. It replaces an <a> wrapped around the card, which makes a screen reader read every word as the link text, and it replaces a click handler on a div.
Code
<article class="card">
<h3><a class="card-link" href="/reports/q3">Quarterly report</a></h3>
<p>Revenue grew in every region.</p>
<button type="button">Save</button>
</article>.card { position: relative; }
.card-link::after {
content: "";
position: absolute;
inset: 0;
}
.card-link:focus-visible { outline: 2px solid transparent; }
.card:has(.card-link:focus-visible) {
outline: 2px solid currentColor;
outline-offset: 2px;
}
.card :is(button, a:not(.card-link)) {
position: relative;
z-index: 1;
}Why it works, the rules and the credits
Why it works
- The pseudo-element belongs to the link and covers the card, so a click anywhere activates the link. It is a real link, so open in new tab and the status bar URL still work.
- The link text stays short. A screen reader announces "Quarterly report" and not the whole card.
:has()moves the focus ring from the small link to the card it activates.- Other controls sit above the overlay and keep their own clicks.
Rules
- No element between the card and the link may be positioned. The overlay would size itself to that element.
- The link's own ring is made transparent, never removed, as One focus ring with
:focus-visiblerequires. Write the full2px solid transparent. Safari ignoresoutline-coloron its default ring. - Text under the overlay cannot be selected. Accept it.
- Put hover styles on
.card:hover, inside the query from Hover styles only where hover exists.
Support
- Chrome
- 105
- Firefox
- 121
- Safari
- 15.4
Credits
Borrowed from: Heydon Pickering, Cards, for the stretched ::after · Stripe, for moving the focus ring to the card with :has()