Clickable Card Patterns and Anti-Patterns
Source: Dev.to
Problem with nesting interactive elements
- HTML Specification – A hyperlink (
<a>with anhref) is interactive content. According to the HTML Living Standard, an interactive element must not contain other interactive or focusable elements as descendants, which prohibits a hyperlink from containing elements like<button>,<input>, or another<a>. - Assistive‑technology issues – Screen readers and keyboard navigation encounter two different links with overlapping hit areas. Tabbing behavior becomes unpredictable, and the link name (as read out by screen readers) becomes the card’s entire contents.
Author: Jamil.
Recommended approach: Stretched Link Pattern
The goal is to keep the primary link (e.g., the card title) separate from secondary interactive elements while using CSS to expand the primary link’s hit area to cover the whole card. Internal links or buttons are positioned above the stretched link so they retain their own interaction priority.
HTML structure
<div class="card">
<a href="/article/123" class="card__main-link">
<h3>Article Title</h3>
</a>
<img src="thumbnail.jpg" alt="Article Thumbnail">
<p>Author: <a href="/authors/jamil">Jamil</a>.</p>
</div>
Key CSS properties
| Element | Key CSS Properties | Function |
|---|---|---|
.card | position: relative; | Establishes the stacking context for absolute children. |
.card__main-link::after | content: ''; position: absolute; inset: 0; z-index: 1; | Stretches an invisible link across the whole card to act as the primary click target. |
Inner interactive elements (a, button) | position: relative; z-index: 2; | Lifts these elements above the stretched link’s hit area, preserving their functionality and focus order. |
CSS implementation
/* 1. Parent element context */
.card {
position: relative;
/* Other styles (border, padding, etc.) */
}
/* 2. Stretched click area */
.card__main-link::after {
content: '';
position: absolute;
inset: 0; /* top: 0; right: 0; bottom: 0; left: 0; */
z-index: 1; /* Less than the inner interactive elements */
pointer-events: auto; /* Ensure the area is clickable */
}
/* 3. Priority for internal interactive elements */
.card p a,
.card button {
position: relative;
z-index: 2; /* Above the stretched click area */
}
Using the Stretched Link Pattern resolves the conflict between design intent (making the card fully clickable) and technical compliance (avoiding nested interactive elements). By preserving correct HTML semantics and managing the stacking context, developers and designers can create robust, fully accessible components that provide a predictable user experience.
Photo by gabby‑k