DEV Community

Cover image for The card mistake you've been making that has been hurting your app's accessibility.
Eyad Abu-Zaid
Eyad Abu-Zaid

Posted on

The card mistake you've been making that has been hurting your app's accessibility.

If you are a frontend developer, or if you have been practicing frontend development either professionally or in your spare time, chances are you have probably created some form of a card in a web application. If you don't know what I'm talking about, a card in this context is basically a UI design pattern that groups related information in a flexible-size container visually resembling a playing card. Think of Netflix's homepage, those small rectangles that represent a movie or a TV series, each one of those is basically a card.

A card from Netflix's website containing information about the movie "The Guilt Trip"

One feature of those cards that is often implemented by developers and designers is that the whole card is clickable and most probably is a link to some other place; a movie page, an article, a post, etc.

That makes a lot of sense; a card is most probably a summary of some content that you think the user might be interested in. It likely contains an image, a title, a brief description, and some other relevant metadata pertaining to the nature of the content. So, you are creating it to lure the user into clicking it to be taken to the full content. Pretty straightforward, right?

The Problem

I have seen a lot of projects over the years, and pretty much ALL the time, I've seen people do the following when developing the card: the whole card should be clickable, so let's wrap the entire content of the card in an <a> tag. Or a <Link> tag if you're using React/Next (Which is just an extension of the <a> tag). And that works just fine. So what's the problem with that?

The problem comes when thinking about accessibility. Think about this, a person using a screen-reader would want the information to be as straightforward and as clear as possible. Have you ever tried using a screen-reader? It's pretty daunting! It reads out the entire content of the webpage. So, if you have a card with an image, a lot of text, and metadata, you, as the user, would have to wait out the assistive technology to read out ALL that content until it tells you that it's actually a link. That has probably already exhausted you just thinking about it.

An Example

Let's go through an example from a web application that I have been absolutely obsessed with. You've probably heard about movie-web.app. It's an open-source streaming web application that uses a clever way to fetch any movie or TV show you would want to stream. I've been spending a lot of time navigating their codebase, so let's take a look at how they have implemented cards. First of all, their cards look like this:

4 cards containing information about Harry Potter's movies from movie-web.app

These are 4 cards, and here's how they implement each one:

import { Link } from "react-router-dom";
  return (
    <Link
      to={link}
      tabIndex={-1}
      className={classNames(
        "tabbable",
        props.closable ? "hover:cursor-default" : "",
      )}
    >
      {content}
    </Link>
  );

Enter fullscreen mode Exit fullscreen mode

As you can see, they wrap the entire card in a react-router-dom <Link> tag. Which, as we already mentioned, is just an extension of the native HTML <a> tag.

Now, it's important to note that in this particular case, that implementation is not much of a problem if any. Because the card contains just the title of the movie, and that's really the minimal information you would need to put inside a simple <a> tag for a movie. However, consider this example of a card from The Guardian's website:

Two cards containing two articles from the Guardian

Now, if those cards were implemented the same way, you can visually see the amount of content the reader will have to read out until it shares the information that all of this is in fact a link. Now you can see the problem.

This is a common pattern that I see the majority of developers mistakenly follow. Because accessibility is generally not that high on everyone's priority list. But it's an issue that needs addressing and needs to be common practice to avoid.

The Solution

Okay. I understand the problem now. But I still need to make a card and I need to make it a clickable link that's also accessible. How can I do that?

It's quite simple really. You just need to be clever with your CSS.

First of all, you probably guessed it, you need to wrap the card in a semantic HTML element such as <article>. Then, you can put an empty <a> tag inside with an aria-label that should take the user to the target page.

Then, to make the whole card clickable, you'll just need to do some simple CSS tricks to make the <a> tag take the whole width and height of the card and also increase its z-index. The HTML/JSX should look something like this:

  return (
    <article
      className="card"
    >
      <h2>{title}</h2>
      <a href={link} aria-label={title}></a>
      <p>{desc}</p>
      <img src={img} alt={title} className="image" />
    </article>
  );
Enter fullscreen mode Exit fullscreen mode

And the CSS should look something like this:

.card {
    position: relative;
}
.card a::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: 1;
}
Enter fullscreen mode Exit fullscreen mode

You should also absolutely create a hover effect to visually indicate that the entire card is clickable, but that's a bit beyond the scope of this article.

Conclusion

Web Development is fun. And, for me, one of the most fun and fulfilling aspects about it is the ability to make something that can be used by anyone, anywhere in the world. That's why while accessibility might be an inconvenience to some developers, it really isn't for me. Just think about this. You're making software that more users will enjoy!

If you have any questions or suggestions, or if I have made a mistake, please feel free to contact me! ❤️

Top comments (3)

Collapse
 
oculus42 profile image
Samuel Rouse

Thanks for the article! Accessibility does not get enough focus.

This does help resolve the accessibility issue, but it creates a separate usability concern for all other uses. The empty link covering the content means we no longer have the ability to interact with any of the elements on the card other than click. No text selection, or saving of images, etc. This may seem minor, but it can have a big impact.

One solution we have implemented is for the card to accept a linkRef or selector for locating the action link within the content, and applying a card-level click handler. While this violates rules around what elements can be interactive, we found it provided a balance, where an action link is provided in the content so accessibility use is preserved, and both the user interaction and the full-card click ability is preserved.

Collapse
 
schmoris profile image
Boris

Interesting! Could you provide a small example? In most cases where click events are attched to an element, there's no option to right-click > "Open in new tab (t)" or middle click (opens in new tab) for power users.

Collapse
 
oculus42 profile image
Samuel Rouse

You make a good point. Our "redirected click" solution depends on a visible action or link in the content, which itself can be interacted with directly for those behaviors. For the application in which it is implemented, Open in new tab is not a desired capability, so we didn't have to support it.

It's definitely a balance of interacting with the card content naturally vs interacting with it as a link.