Accessible Clickable Card options
Card components are one of the most common places where visual design and accessible markup pull in different directions. Designers want the whole card to feel clickable; accessibility and HTML semantics want one clear, single interactive element. Each method below uses the same basic card content, but changes how (and whether) the whole card responds to a click or tap.
Every example card contains the same four things: a card container, an image, an <h3> heading, and a short paragraph.
Method 1: Just the default – users click the link in the heading
No extra markup or scripting. The only interactive element is the link inside the heading.
Known for its distinctive call, this large kingfisher is native to eastern Australia.
Accessibility features
- Only one interactive element on the card, so its accessible name is unambiguous.
- Completely standard keyboard behaviour: Tab to focus, Enter to activate.
- All native link behaviours work automatically – open in new tab, copy link address, drag to bookmark, browser status bar preview.
- Nothing to build or maintain; works even if CSS or JavaScript fails to load.
Possible issues
- The clickable target is small – just the heading text – even though the whole card looks like a single unit.
- Users may click the image or the paragraph expecting to navigate, and nothing happens.
- On touch devices especially, a bigger hit target is usually expected for a "card" pattern.
Method 2: An <a> wrapped around the entire card
The whole card is placed inside one big link, and the heading still has its own link inside it.
Kangaroo
Kangaroos possess powerful hind legs, a long, strong tail, and small front legs.
Accessibility features
- Large, obvious click/tap target covering the whole card.
- Still uses a real
<a>, so it is keyboard-focusable and works with assistive technology out of the box.
Possible issues
- A link inside another link (
<a> nested in <a>) is invalid HTML. The content model does not allow interactive content inside a link.
- Browsers don't reject this – they silently "fix" it during parsing, using the HTML parser's adoption agency algorithm. Inspect this card in devtools and the one outer link has been split into disconnected fragments: a clone wraps just the image, an empty clone sits next to the real heading link inside the
<h3>, and the paragraph is left completely outside any link. The "click anywhere on the card" goal is silently broken by the browser itself, with no error or warning.
- If the card contains any other interactive element (a second link, a button, a "read more"), it must sit outside the big link entirely, or it becomes unreachable/unusable.
- The accessible name of the big link is the concatenation of everything inside it (image alt text, heading, paragraph), which can be a long, awkward announcement for screen reader users.
Method 3: CSS used to expand the link to cover the card
Only the heading has a real link. CSS uses an absolutely-positioned ::after pseudo-element on that link to visually stretch it across the whole card (sometimes called the "stretched link" pattern).
The emu is the second-tallest living bird after its ratite relative, the ostrich.
Accessibility features
- Only one real link exists in the markup, so there is no nested-link problem and only one, clear accessible name.
- Full native link behaviour (new tab, copy address, status bar preview) still works.
- Large visual/click target without any JavaScript.
Possible issues
- The card container (or another ancestor of the link) needs
position: relative, and the link itself needs an ::after pseudo-element with position: absolute and inset: 0, which is easy to get wrong if the card is later restyled.
- Any other interactive element added to the card later (a second link, a "share" button) will sit underneath the stretched pseudo-element and become unclickable unless it is explicitly given a higher
z-index.
- Text inside the card (other than the heading) cannot be selected with a mouse, because the invisible layer intercepts the click/drag.
Method 4: JavaScript finds the link and makes the whole card clickable
The heading keeps its own real link. A script listens for clicks anywhere on the card and, if the click didn't land on an interactive element, triggers the heading link instead.
This large, stocky marsupial is found in Australia and on scattered nearby islands.
Accessibility features
- Only one real link in the markup – the card itself is not made focusable, so there are no duplicate tab stops.
- Keyboard users tab straight to the real link and use it normally; nothing custom to get wrong.
- If JavaScript fails to load, the heading link still works – the card just loses the "click anywhere" enhancement.
- Can be written to ignore clicks on other interactive children, and to ignore click-drag text selection so it doesn't misfire.
Possible issues
- Entirely dependent on correct, defensive JavaScript. It's easy to forget an edge case (text selection, middle-click, modifier-key click) and end up with confusing or broken behaviour.
- Middle-click / ctrl-click / cmd-click "open in new tab" on the empty part of the card won't work, only a real click on the actual link supports that – this can surprise users who expect the whole card to behave like a link.
- Adds maintenance cost and a script dependency for something CSS alone can usually do (see Method 3).
Method 5: A single link with no separate heading link
Instead of nesting a link inside a link (Method 2's problem), the whole card is the only interactive element. The heading is plain text, not a link.
Tasmanian devil
The world's largest carnivorous marsupial, now found in the wild only on the island of Tasmania.
Accessibility features
- Valid markup with no nested interactive elements, and a single, predictable accessible name and tab stop.
- Large click/tap target, full native link behaviour, no JavaScript required.
- Avoids Method 2's silent DOM breakage entirely, by design rather than by accident.
Possible issues
- Only works cleanly for cards with a single destination. As soon as a card needs a second link or a button (e.g. "add to favourites"), that control has to be pulled outside the big link, which changes the visual layout.
- The heading is no longer a link in its own right, so it can't be usefully listed in a screen reader's "links list" independently of the rest of the card content.
- The whole card's accessible name is built from all of its content (image alt, heading, paragraph), which can be long-winded unless it is trimmed with
aria-label.
Method 6: A non-link element with a click handler (fake button/link)
The card container itself gets a click handler (and often tabindex and a keydown handler) so it behaves like a link, without ever using a real <a>.
Accessibility features
- Can be made to work with a screen reader if
role="link" (or role="button"), tabindex="0", and matching keydown handling are all added carefully.
- Large click/tap target with full control over styling and behaviour.
Possible issues
- None of the native link behaviours exist unless you reimplement them yourself: no "open in new tab", no "copy link address", no status bar URL preview, no drag-to-bookmark, no middle-click.
- Like Method 5, the accessible name is built from all of the card's content (image alt, heading, paragraph), which can be long-winded unless it is trimmed with
aria-label.
- Search engines and other automated tools that only follow real
<a href> links won't discover the destination.
- Every keyboard interaction (Enter and Space for a button role, Enter only for a link role) has to be coded and tested by hand – it is very easy to only handle click and forget keyboard users entirely.
- If JavaScript fails to load, the card is completely non-functional – there is no underlying href to fall back on.
- This pattern reinvents something the browser already provides for free with a plain
<a>, for no real benefit – generally best avoided in favour of Methods 3 or 5.