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