The other day I posted an image, quite literally as a thought exercise, about how you might accomplish “nested” links. That is, a big container that is linked to one URL that contains a smaller container or text link inside of it that goes to another URL. That’s harder than it might seem at first glance. The main reason being that…
<!-- this is invalid and won't render as expected -->
<a href="#one">
Outside
<a href="#two">
Inside
</a>
</a>
Eric Meyer once called for more flexible linking, but even that doesn’t quite handle a situation where one link is nested inside another.
Here’s what happens with that HTML, by the way:
My first inclination would be to simply not nest the links in the markup, but make them appear nested visually. Some folks replied to the tweet, including Nathan Smith, who shared that same thought: have a relatively positioned parent element and absolutely position both links. The larger one could fill up the whole area and the smaller one could sit on top of it.
See the Pen “Nested” links by Nathan Smith (@nathansmith) on CodePen.
It’s finicky, as you’ll need magic numbers to some degree to handle the spacing and variable content.
My second inclination would be to deal with it in JavaScript.
<div
onclick="window.location='https://codepen.io'"
style="cursor: pointer;"
tabindex="1"
>
Outside
<a href="https://css-tricks.com">
Inside
</a>
</div>
I have literally no idea how kosher that is from an accessibility perspective. It looks gross to me so I’m just going to assume it’s bad news.
Speaking of accessibility, Heydon Pickering has a whole article about card components which is a popular design pattern where this situation often comes up. His solution is to have a relatively positioned parent element, then a normally-placed and functional main link. That first link has an absolutely positioned pseudo-element on it covering the entire card. Any sub-links are relatively positioned and come after the initial link, so they’d sit on top of the first link by way of z-index
.
And speaking of stacking pseudos, that’s the approach Sean Curtis uses here:
See the Pen Pretend nested links by Sean Curtis (@seancurtis) on CodePen.
Other solutions in the “crafty” territory might be:
- Using a
<form>
element where theaction
attribute acts as a link. - Using an
<object>
element to wrap the inner link.
Sara Soueidan responded with her own post!
I had the same requirement a couple of years ago when I was building the front-end foundation for Smashing Magazine. So I thought I’d write my response to Chris’s thread out in the form of a blog post.
Sara has written about this with much more detail and care than I have here, so definitely check that out. It looks like both she and Heydon have landed on nearly the same solution, with the pseudo-element cover that contains sub-links poking above it as needed.
Have you ever done it another way? Plenty of UX and a11y to think abbout!
Well, this is a bizarre thing to want to do! First of all, philosophically, it is a UI issue – bad design visually (and semantically!). If. there is ever a ‘real world’ need for this, redesign the component! (I’ve come across the same conundrum, which is the only reason I’m commenting). BUT, for academic reasons… I think you are vastly overcomplicating the problem. The rock solid way to do something like this is with ye olde image map (for those too young to know what this is: https://www.w3schools.com/Tags/tag_map.asp). I haven’t tested it, this is theoretical, so I don’t know what gets prioritised if you wrap an image in an href and add an image map, but… Plan B would be to anchor the image itself in relative positive, then absolute position clip-path ‘hot zones’ (higher z-index) for your nested image/links. Old skool.
But why, why why? :-)
“Your scientists were so preoccupied with whether or not they could that they didn’t stop to think if they should.” -Dr. Ian Malcolm.
I know this was a thought exercise, but from a user experience standpoint, I think it’s probably for the best that there’s no simple way to do this. The potential for confusing behavior is just far too high.
We’ve recently encountered this requirement (again) at the Telegraph, we sort of had a green-field on the frontend for this body of work.
We’ve gone with the z-index solution to the layering issue, but went with using JS to take any given link and turn one of its ancestors into the clickable block.
Therefore without JS all links function with their default behaviour, whilst the behaviour and pattern is easily re-usable in different contexts as well as enforcing a basic level of a11y by design (hopefully). It also means we can (though hopefully won’t) have nested clickable blocks!
I’ll try and do a quick write up later come back here and link to the code in production … and therefore probably benefit from a large set of some excellent testing and feedback :)
Interesting approaches. I liked the approach that Material design specifies for dealing with secondary actions on cards, which they show at https://material.io/design/components/cards.html#actions
The brief version is that the top 2/3 or so of the card is a primary action space for the card’s main topic, and that the bottom 1/3 or 1/4 of the card is not clickable in its own right, but contains additional calls to action.
Forgot to add that the demo of the web version of the card component at https://material-components.github.io/material-components-web-catalog/#/component/card is really good for showing how this behaves via tabbing, touch, and mouse.
David, I like the material.io implementation.
But… as is so often the case, you get a designer (or someone else higher up the “food chain”) insisting that the whole card should be clickable! Grrrr.
Can CSS Grids be used for this? Have both link elements as direct children of the grid, with one positioned from edge to edge, and the other only occupying a single cell of the grid?
Sure! That works!
My approach to this has always been to gently try and convince the client that what they want to do is a bad idea. I’ve ended up implementing it anyway twice, once because we were hired as extra development hands and they already had an established pattern, and once because the argument was going nowhere. Both times used pseudo elements absolutely positioned inside a relative parent “card”
Use nested elements to represent structure and inside the elements you can put the links. Any HTML element, which can be nested can contain links as inner HTML and use CSS to give it visual appeal.
We had this problem as well on our search results page.
Card-like display, Image, title, summary and a few buttons.
You should be able to click the whole card for the details or the buttons for direct actions.
Keeping accessibility in mind, we wanted no double links and the card title had to be a clickable header.
So the reader would hear: Card title, button 1, button 2. While a mouse user can still click the whole card or individual links.
For this we added an additional details link to the card and gave it a tabindex=-1 to prevent it from being a TAB stop.
Then positioned it to to cover the whole card and z-indexed it under the buttons.
It’s always good to test your page without a mouse. Does it still make sense if you (shift-)TAB through it?
Or install screen reader (NVDA is free for example) and get a first hand experience on how confusing it might be ;)
I’m quite content with the approach I came up with for https://fastmail.blog/, where each blog post’s “card” is
position: relative
, and there are three or so distinct links inside it, but that the entire “card” acts as the link inside the h2. This is done with an::before
on that link with what is approximatelyposition: absolute; top: 0; left: 0; right: 0; bottom: 0;
, and giving the other links a z-index so that they can sit on top of it. The end result is successful for accessibility and normal use, without needing any JavaScript. The only caveat is that you can’t readily select the other text—but that’s roughly par for links anyway, and so not a big deal.You can create nested links by moving them in the DOM with jQuery
Isn’t using CSS to set the outer
<a>
‘s display property to block or inline-block will solve the problem? I remember doing so in the past and it worked.I can’t imagine any browser where that ever worked. It’s not that you can’t set that property, it’s that the DOM will boot the inner one out. Give it a shot.