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;
}
Related articles

Exploring responsive grid systems for complex but structured layouts.

2018
CSS UI patterns
HTML + CSS

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

2018
CSS UI patterns
HTML + CSS

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

2017
Firefox
HTML + CSS