Implementing accessible clickable cards
A common web pattern is the clickable card that navigates users to more detailed content.
I used to wrap the card in a link like this to make it clickable:
<a href="/link" target="_blank>
<section className="property-card">
<h2>Luxury living packed into a convenient apartment</h2>
<span>1 bed | 1 bath | 1 carpark spot</span>
<p>Enjoy the best of Brisbane from this riverside haven. See if this studio apartment in South Brisbane is right for you.</p>
</section>
</a>
Until I covered Scrimba's accessible development module in their front-end development career path where I learnt how the above works but…
Why wrapping the card in a link can be an issue
When an entire card's content is placed in a link, a long string of text content will be read out before the person is told that it is a link.
Screen readers can extract and list links on a page. When a link contains a large block of text from a card, it becomes a long, less digestible entry in that list.
Meaningful, standalone link text (e.g., "Explore accessibility resources") is preferred over lengthy card content.
Place links within the card instead
Step 1 in HTML
- Place an empty link at the end of card content
- Add aria-label for screen readers
<section className="property-card">
<h2>Luxury living packed into a convenient apartment</h2>
<span>1 bed | 1 bath | 1 carpark spot</span>
<p>Enjoy the best of Brisbane from this riverside haven. See if this studio apartment in South Brisbane is right for you.</p>
**<a
href="/link"
target="_blank"
className="property-card-link"
aria-label="Learn more about this studio apartment in South Brisbane">
</a>**
</section>
Step 2 with Tailwind CSS
- Size width and height of the link (
property-card-link
) over the entire card. - The
:after
pseudo-element withcontent: ""
and absolute positioning makes the entire card clickable, sitting on top of the content (z-10
). - The
cursor-pointer
on the card indicates clickability. - Test focus states (I removed it from the link because the
project-card
handles the focus state)
.property-card {
@apply relative bg-white hover:bg-stone-50 cursor-pointer;
&:has(:focus-visible) {
@apply outline outline-2 outline-offset-4;
}
}
.property-card-link {
@apply w-full h-full;
&:after {
@apply absolute inset-0 z-10;
content: "";
}
&:focus-visible {
@apply outline-none;
}
}
All notes