As we noted in our complete guide, you can put an <a href="">
link around whatever chunks of HTML you like. Let’s call that a “block link.” Like you are wanting to link up an entire “Card” of content because it makes a big clickable target.
<a href="/article/"> <!-- display: block; -->
<div class="card">
<h2>Card</h2>
<img src="..." alt="...">
<p>Content</p>
</div>
</a>
On that, Adrian Roselli:
Perhaps the worst thing you can do for a block link is to wrap everything in the
<a href>
.
[…] for a screen reader user the entire string is read when tabbing through controls. In the following example, the first link contains the heading, image (without declaring it as an image), and block of text, taking about 25 seconds to read before announcing it as a link. When tabbing, you do not always know the control type until the accessible name is complete.
(The example is a pretty normal looking card with a header, image, and paragraph.)
So don’t do that.
The alternative is to let the link be “normal” like just the header.
<div class="card">
<h2><a href="/article/">Article</a></h2>
<img src="..." alt="...">
<p>Content</p>
</div>
The extending the “clickable area” of the link to cover the entire area.
.card {
position: relative;
}
.card h2 a::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
That works for the clickable area and solves the penalty to screen reader users.
But there is another problem that hurts both of these solutions, and it’s text selection. You can’t just put your cursor somewhere in the card and select text normally. The click activates the link, waiting for you to mouseup
while still on the link to trigger it. You don’t get the ability to select inner parts of the text as you would probably expect. It doesn’t prevent the ability to select the text at all, but it makes it awkward and annoying.
I’m not sure that is easily solveable. Perhaps there is some exotic JavaScript solution that can detect if you’ve started to select text and then not trigger a click, but if you click without dragging then it does go to the link. Something about that is a bit red-flaggy to me though.
Update: Check out Heydon’s “The redundant click event” section of his Cards article.
All in all, I’d say block links are just a bad idea. But I’d love to be proven wrong and see a really good implementation that solves all these issues.
Can’t we solve the selection issue in the second solution with a user-select: none on the ::after pseudo-element? Will give this a shot tomorrow.
Make the whole block clickable but not included in the link?
From what I’ve been reading I think that screen readers don’t detect this. But maybe I’m totally misunderstanding that?
Just tried user-select:none, no dice as it still intercepts pointer events.
This is the closest I got taking a stab at it: https://codepen.io/dougwollison/pen/poJagLJ
Basically by layering the psuedo element behind everything else, the text is selectable, while any other part of the card triggers the click, but any decorative stuff like borders, icons inside the text elements (that aren’t appropriately z-indexed), and expecially any padding on text elements, will cover up clickable space.
Honestly given how inconsistent this can appear to a user for areas that feel empty (e.g. corners of text elements devoid of text), it probably does more harm than good.
Then you’re back where you started and the card isn’t clickable.
Won’t work since the pseudo-element will be on top of everything. The user-select property controls text selection only. If anything is “above” the text, you can’t select it anyway :(
Could the z-index change on the ::after upon hovering on the card?
You can but if your force z-index on your text, you won’t be able to “click” on it. You will just be able to select it. We want the whole card clickable and all the content selectable :(
You could use a mix of
aria-labelledby
andaria-describedby
to create a linkable element which is labelled quickly as the heading element, and then has additional description by the paragraph?Then by adding
draggable="false"
to thea
tag, it makes the text selectable in firefox (which allows you to drag links, making the text within ana
unselectable)Codepen example here: https://codepen.io/mikeybinns/pen/LYVQEyz?editors=1100
Mikey, I have a comment below discussing the risk of using ARIA here. Essentially it will override the real link text, obfuscate the remainder of the content, and strip the semantics. Probably do not rely on
aria-labelledby
andaria-describedby
here.Block wraps inline for hierarchical reasons. Inline should not wrap block. CSS has made it possible to display:block an A link so there isn’t much need to break hierarchy.
This lets the user click anywhere on the card to visit the URL while also allowing them to select text:
Link text isn’t selectable (except by region selecting from outside the link), but using ‘Read more’, ellipsis, or even an invisible link would still work.
Is it sinful to suggest that screen readers could be improved? And regarding text selection, that’s a long standing problem for “normal” links as well, and it’s exacerbated as you described for block links. I continue to quietly wish for browsers to allow a alt-key holding solution to disable links while I select text.
I’m not sure I agree that block links are a bad idea. Although to text selection issue is indeed not ideal, when watching ‘normal’ web users navigate a website (particularly on mobile) people expect block links, or at least to be able to click on the image, title, description and anything related in the vicinity.
The number of people that are selecting text from within a block link I would imagine is fairly minimal, I cannot remember a time when I have done so. Compare that with the user experience impact of people clicking on a link image and waiting for the page to load, despite the fact it isn’t because they had to click on the heading – or the accessibility annoyance of having every element individually a link.
Here is how I usually handle “block links” : https://codepen.io/Manoz/pen/NWqybdN
I’m using a link tag and style the pseudo-elements just like you do in this article.
The only thing that changes is when I want some elements clickable (tags for example) I just play with z-index on them.
Sill stuck on that “text-selection” issue tho.
http://inclusive-components.design/cards/
Similar post, more answers
I like how Heydon tackles the text selection problem with JavaScript:
With the “redundant click event”-solution I am facing the problem that the url isn’t shown on hover.
That’s something I am used to as a user to check where the link is guiding me.
Any idea how this could be worked around?
You do not have the problem when you use the pseudo-element approach, but then – once again – you cannot select text.
Recently discovered this and was super excited about the “redundant click event”. Only when a colleague implemented it I realized that this doesn’t allow users to open the link in a new tab anymore. Felt super weird. We dropped it again and haven’t found anything better yet.
This has come up often in our a11y tests. We’ve solved it by adding a data-href attribute and click handler to the container. We let clicks inside the container bubble up unless they are on actual
<a>
elements. This treats clickable thumbnail images as an enhancement while avoiding a11y issues with duplicate links. You can abort navigating if the user is selecting text by checking if window.getSelection().type === “Range”. So far it seems to work pretty well!Funny I just built exactly this setup yesterday and was thinking I really shouldn’t be doing this. Now I feel bad and will go fix it!
I don’t have an example handy and I haven’t done one of these this way in a while, but I believe if you attach a click event to the “.card” that finds the within it and then opens the link, you can still select the text.
Something like this if you have jQuery available:
(Mobile devices really need to add a “code” keyboard that uses straight quotes instead of curly ones! not going to spend the time to fix those now…)
Since this functionality is really just a convenience for the user it’s OK that it potentially doesn’t work due to JS errors or JS being disabled. There’s still a clickable element there.
But things may have changed, like I said it’s been a while and touch events have seriously changed how these bits work since I last did that.
Text selection is probably a minor concern in many use cases of this structure though, these are probably navigation elements and more complete text (Meaning a better source of text to copy) is likely available after clicking the link anyway?
I’ll probably just go with the second option – somebody wrote up a description of that pattern last year calling it a “breakout button” I think because it pops the clickable area out of its normal location. Simpler, no javascript, fully accessible.
https://developer.mozilla.org/en-US/docs/Web/CSS/user-select
user-select maybe something??
Would
role="presentation"
oraria-hidden
work on one of these elements? Old timwright.org post: “…role="presentation"
will not remove content or semantic definition from focusable elements. That means your links, buttons, and inputs will remain as they should, along with all the content…”Couldn’t this be solved with
aria-labelledby
andaria-describedby
? Or am I missing something? Example: https://codepen.io/falldowngoboone/pen/LYVdvQx.I am genuinely curious about this because I’m putting together a talk on accessible patterns.
Looks like a good idea for labeling but doesn’t do much for text selection.
I’ve updated the CodePen to reflect this. Very informative. Thanks!
Ryan, the issue here is that the
aria-labeledby
overrides all the content in the link. Addingaria-describedby
only mitigates this a little since it is announced only after a pause. A screen reader user often does not wait for more and while tabbing around the page may miss that additional context.Both attributes also ignore any semantics when announcing the value to users. If your card has a list or sub-heading or any other content with structure or semantics, that information will not be conveyed.
Bear in mind that some screen reader users will arrow through the links and not be blocked by this. I talk about this and some other challenges in responses to ARIA-related questions on my post.