https://github.com/openui/open-ui/issues/
We are currently incubating and exploring a new idea. Chime in!
Historically and for very good reasons, interactive elements on the web cannot be nested within other interactive elements. Examples of interactive elements include links and buttons. Interactive elements (such as buttons and hyperlinks) not allowing descendent interactive elements is a well established rule in HTML - where interactive elements or elements with a tabindex
attribute within such elements are specifically called out as invalid (for instance, see the a
element’s content model. Additionally, the ARIA in HTML specification provides additional guidance to discourage the use of interactive elements as descendent of ARIA roles which are themselves considered “widget” (interactive) roles.
Despite this long-standing rule, there are websites which ignore it. In doing so, these sites end up with UI which can be difficult if not impossible to use with assistive technology, such as a screen reader. On the flip-side, there are websites which have created clickable containers in a way that passes HTML validation, and often with the goal of making the pattern, at least, keyboard and screen reader accessible. This can be achieved by implementing such markup patterns in specific ways, along with using CSS, JS, or both to extend a primary target’s (often link, sometimes button) clikcable area. The end UX/accessibility of such endevors is commonly variable, due to a lack of standardization and common lack of knowledge about how assistive technology accesses content through the browser’s accessibility APIs.
As some quick examples of websites which have variant instances of such patterns, one can look for them on Walmart, Best Buy Reddit, and Amazon, to name a few.
Commonly a clickable container, often referred to as a “card”, will present a graphic or other media, text, a primary call to action (link or button) as well as other supplementary interactive elements:
Today, making a container/card clickable can be achieved with a few different approaches, each with its own tradeoffs:
a
element to wrap the content that needs to be clickable. The issue with this is: a. doesn’t support nested interactive elements without creating accessibility issues. b. using an a
element to wrap content is not supported in certain contexts - e.g., such as trying to wrap a row within a table. The HTML parser will remove such hyperlinks from the rendered table markup. c. even without nested interactive elements, the accessible name of the hyperlink can become overly verbose, or may not be exposed as all depending on the roles of the elements that are descendents of the hyperlink.::before
or ::after
pseudo element “behind” the “nested” interactive elements, so that the link catches clicks that are not handled by these other interactive targets. The issue with these can be: a. again, no standardized way to do this, with some version of this implementation better than others. For instance, implementations that use duplicate links, creating redundancy for keyboard and/or screen reader users (even if the duplicate link is tabindex=-1
), being some of the worst. b. this sort of stacking/overlaying implementation often makes it difficult if not impossible for some users to select what appears to be static text, and can lead to some instances of accidental activation of the hyperlink. For instance, due to lack of interactive affordance that the static text / white space areas of the component are clickable. c. Such a pattern usually relies on all intermediate elements between the container/card and the primary action within to have display: static
. Depending on the visual design requirements, this may not be feasible.mousedown
rather than ‘up’, or people forgoing the inclusion of a nested hyperlink in favor of only using the JS, leading to the element being accessible to pointer events, but often not to keyboard or assistive techology users. c. additionally, some JS implementations have the potential to conflict with default browser behaviors - such as context menus, or popup blocking.These are ways to achieve this kind of experience that have their own constraints and compromises. At this point, this became a common enough pattern in the web, so perhaps it is time it became a first class citizen.
At a high level, the proposed mental model of this problem is “the container is delegating its interactive area to one of its link descendants”. By introducing a standardized feature to allow a container to be “clickable”, allowing a user to activate a required nested target within the container, web authors can be provided a solution that mitigates the pitfalls of currently available solutions (transparent hyperlinks), as well as the less desireable UX and variability of the CSS positioning/stack or JavaScript approaches available today.
Some more detailed points on what this means for the UI and UX of the feature:
popover
attribute).label
element is not overtly exposed to the accessibility tree/to assistive technology users. It’s purpose is to extend the clickable area of the referenced hyperlink - which is expected to be the accessibile object for people using AT/keyboard to interact with. a. NOTE: some screen readers, such as NVDA, may announce “clickable” for current instances of the JS variant of this pattern. This is not necessarily because it’s important for users to know the container is clickable in this specific context - but because the screen reader is only aware there is a click event on the container, but not what the click event will do. If the screen reader were aware of the relationship between the container and the nested hyperlink, and that they would perform the same function, it is far more likely that such instances of the “clickable” announcement would not be needed.Expressing link area delegation in HTML can look something like this:
<div class="card" linkarea>
<a href="/post?id=123" defaultlink>Post Title</a>
<img ...>
<button>Join</button>
<button>Share</button>
</div>
In the above example, the div
element has a linkarea
attribute, which would delegate its click to the first descendant with a defaultlink
attribute, in this case the first a
element.
Additionally, we could have a similar alternatives to this with different ergonomic tradeoffs, e.g., using ID references:
<section class="card" linkarea="card123-cta">
<h2><a href="#topic-123" id="card123-title">Post Title</a></h2>
<img ... alt=">
<p>...</p>
<a href="/post?id=123" id="card123-cta" aria-labelledby="card123-cta card123-title">Learn More</a>
<button>Join</button>
<button>Share</button>
</section>
In this modified example, the first hyperlink now serves as an in-page permalink. The primary action instead being the “Learn More” link, whose id
is referenced by the containing section
element’s linkarea
attribute.
Note that in this example, the hyperlink still has to be a descendant of the card. This is to guide web authors to an interactive structure where the links are in the appropriate place in the tree.
One of the alternatives that may initially sound natural is a new element type:
<section class="card">
<linkarea>
<a href="/post?id=123" defaultlink>Post Title</a>
<img ...>
<button>Join</button>
<button>Share</button>
</linkarea>
</section>
This can feel compelling in a semantic sense, but becomes undesirable when we think of the flexibility of attributes vs HTML elements. For example, a use case for link area delegation is making table rows clickable.
<table>
...
<tr linkarea>
<th><a href="/details?id=1" defaultlink>Title</a></th>
<td>...</td>
<td>...</td>
<td><button>Edit</button></td>
</tr>
...
</table>
This would likely not be possible with a new element, as a tr
within a table
(and some other container elements) have special HTML parser behavior. Modifying their parsing behavior to include new types of elements would break the page in browsers that don’t support the new element type. Whereas taking the attribute approach, legacy browsers could support the new behavior via a polyfill, mitigating any parsing issues that might otherwise result in unwanted DOM rendering.
Since this proposal focuses on click areas, another option might be using CSS rather than HTML? Instead of a new attribute, providing a new CSS property:
<section class="card">
<a href="/post?id=123">Post Title</a>
<img ...>
<button>Join</button>
<button>Share</button>
</section>
.card { pointer-area: contain; }
.card a:nth-child(1) { pointer-area: expand; }
This has nice ergonomics because the selection of link area can be done outside of the main structure of the page using CSS selectors. However, something about this feels limiting, as it is constrained to click areas in advance, and doesn’t lend itself to future UA enhancements of this experience, such as context menus. Identifying the hyperlink or interactive control to expand its click area might also become difficult if needing to apply this feature to markup beyond one’s immediate control - e.g., syndicated content created by other product teams.
An interesting alternative from @jaffathecake is to use invokers. The prior “card” example has been modified to use commandfor
and commands
attributes, instead of a linkarea
attribute or CSS:
<section class="card" commandfor="card123-title" commands="click contextmenu">
<a href="/post?id=123" id="card123-title">Post Title</a>
<img ...>
<button>Join</button>
<button>Share</button>
</section>
This a nice idea as it could allow delegation of clicks to more than just “links”. For instance, delegating to a button
element instead. The tradeoff here might be that this would need two attributes to declare, rather than just one. And that the id
of the desired element to delegate to might be necessary to declare more often than the common case of extending the click-area of the first hyperlink within a container.
Regarding click delegation to links (and potentially buttons) there should be no “new” accessibility concerns with this proposal. As mentioned, the semantics of the containing element would not be expected to be altered, so there should be no unexpected modifications to the exposure of the element in the accessibility tree. Optionally, a new a11y object attribute could be added in the accessibility mappings so AT could do something special with it, if they so chose.
If this proposal (as an attribute) were to be accepted, there would likely need to be new rules added to the ARIA in HTML spec, limiting the allowed roles for elements this attribute was used with. E.g., <div role=button linkarea>
- presently a div
allows for any ARIA role to be set, but this instance should likely produce a validation message of some sort.
One existing accessibility issue that can be exacerbated by extending target areas (and exists with any of the current non-standardized methods) is the potential to create targets that are too close together / small in comparison to the entire clickable area of the container/card.
This is indeed a concern, but at least with a standardized approach the following is more likely to occur:
linkarea
container could be made. These should be able to be overwritten, but doing so could then produce browser console errors to warn authors they have undersized targets within their larger clickable container. Console errors could be provided regardless of any UA style changes as well, since the height/width of targets is already known by the browser, and thus if undersized targets exist within a clickable container, this could already be known/exposed as an author error.For more information, please review
Link area delegation is a common pattern on the web. This proposes graduating it from a userland CSS/JS issue to a first class web platform primitive. Given that, we have to be careful to make the right choice around accessibility and ergonomics, especially around target sizes for interactive elements within the nested clickable container having the potential to not meet WCAG requirements.