Block-level links

A practical and reusable technique for improving the accessibility and styling of block-level links and buttons.

Typical block-level links

A block-level link or button is one that wraps block-level content. An extremely simple example is as follows: <a href="/"><div></div></a>. This type of link is incredibly useful when creating a tactile user interface, with large click targets for complex items that have their own internal structures.

Take an e-commerce site’s product listing page. Products could be laid out in a grid of boxes, with each box containing a product’s name, details, and image. We could simply make the product name a link, but this would provide a relatively small touch target. If the box provides a visual affordance that it links to the product page, we could wrap each product listing in an <a> element.

Addressing the downsides

Although wrapping a whole block item in a link quickly produces a rough version of the desired behavior, it has two main pitfalls:

  • CSS side effects: Any elements within a link inherit the browser’s default text-decoration: underline;. To remove this, we need to add a style to any container component that might be used as a block-level link. The components would need to be aware they’re be used as block-level links. That’s alright, but it starts to get more complicated when we need to remove all of the default styling from a block-level <button> element and add a specific border or background back onto the component. Removing default styles and having custom, component-specific styles are at odds with each other.
  • Screen reader overload: Lengthy content within a link or button may be read aloud when a screen reader user focuses on that link. It would be preferential to read only a short title and allow the user to engage with it to hear more.

Faux block-level links

Taking into account the utility of this sort of link or button, what can we do to make it more robust — to work around the disadvantages of nesting a large amount of content inside a single link?

We’ll need to identify and style two elements:

  • The containing area: a position: relative; positioning context element that replaces what would otherwise be a block-level link. This element may or may not have other visual styles, independent of its role as the containing link area.
  • The link area: an invisible position: absolute; link filling the containing area. Pay attention to the order of this element in the document outline, as if it is placed above its corresponding heading, a screen reader user may not notice its presence.

Example implementation

I like to have two implementations of this at the ready: one for links, and one for buttons. Both can be necessary due to their subtly different semantics. For brevity, the following example uses a link:

<div class="card isContext">
    <h3 class="hdg hdg_minor">Product Name</h3>
    <a class="blockLink" href="">
        <span class="isVisuallyHidden">View Product Name</span>
    <p>Product description.</p>
         alt="Photo of Product Name" />
// Demo styles for product box: 
.card {
  padding: 1rem;
  background: #f0f0f0;
// The link area / positioning context: 
.isContext {
  position: relative;
// Text content for screen readers to identify the link: 
.isVisuallyHidden {
  position: absolute;
  left: -10000px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
// The faux block-level link: 
.blockLink {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
.blockLink:focus {
  border: 1px dotted;
Related articles

Exploring responsive grid systems for complex but structured layouts.

CSS UI patterns

Using the flag or media pattern for locking up graphics and text for comments, alerts, and more.

CSS UI patterns

A hidden element within a link can stretch the link’s focus outline to the edge of the viewport — and beyond.