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="http://example.com/product">
    <span class="isVisuallyHidden">View Product Name</span>
  </a>
  <p>Product description.</p>
  <img
    src="https://placeholdit.imgix.net/~text?txtsize=33&txt=image&w=150&h=150"
    alt="Photo of Product Name"
  />
</div>
// 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;
}