Skip to content
Protocore Design Systemv1.6.9

/// Layout

LinkOverlay

A stretched-link helper that makes a whole Card or Tile clickable through one keyboard-correct anchor.

import { LinkOverlay, LinkBox } from "@protocore/pds";
View as Markdown

A clickable card

Wrap the surface in LinkBox (which sets position: relative) and drop a LinkOverlay anywhere inside. Its ::after covers the whole box, so the entire card is the click target — but the DOM still holds exactly one real anchor, so keyboard and screen-reader behaviour stays correct.

With a secondary action

A second interactive element inside the box (a button, an outbound link) stays clickable by raising its own stacking context — position: relative; z-index: 1. It sits above the overlay while the rest of the card still routes to the primary link.

Why not wrap the card in an anchor

This is the affordance-honesty law (§7.2) made reusable. Wrapping a whole card in an <a> is invalid when the card contains other links or buttons, and an onClick on the surface is invisible to the keyboard and to assistive tech. The stretched-link pattern gives you one real, focusable, announceable anchor whose hit area is the entire surface.

LinkOverlay supports asChild, so you can hand it your framework's Link (Next, Remix) and keep client-side navigation. Give the surface a visible hover/focus affordance only when it is genuinely interactive — the whole point is that it now is.

Usage

Do

  • Wrap the surface in `LinkBox` (or set `position: relative` yourself).
  • Keep exactly one primary `LinkOverlay` per box.
  • Raise secondary actions with `position: relative; z-index: 1`.
  • Use `asChild` to pass a framework `Link` for client navigation.

Don't

  • Wrap the whole card in an `<a>` — invalid with nested interactives.
  • Put two overlays in one box; the hit areas will fight.
  • Add an `onClick` to the surface instead; that skips the keyboard.

Accessibility

KeysAction
TabMoves focus to the single overlay anchor
EnterActivates the link
  • Exactly one anchor is in the accessibility tree, so screen readers announce one link for the whole surface.
  • The focus ring is moved onto the stretched `::after`, so keyboard focus outlines the entire surface rather than a zero-width inline anchor.

LinkOverlay props

PropTypeDefaultDescription
asChildbooleanMerge props onto the single child instead of rendering an `<a>` (framework links).
classNamestring
styleCSSProperties

LinkBox props

PropTypeDefaultDescription
classNamestring
styleCSSProperties

Related

  • CardA surface block with a hairline border and sharp corners — flat title/subtitle/action props or compound Card.Header/Body/Footer.
  • ProductCardAn illustrated product card — art panel, eyebrow, title, one-line body, a mono tag row, and an outbound footer link, with a [ 0N ] index.
  • ListingRowA scannable list row — mono title, mono meta columns, and a trailing outbound arrow; hover dims to 0.6.
  • LinkInline text link — ink with a control-hairline underline that inks in on hover.