{
"$type": "site.standard.document",
"content": {
"$type": "site.standard.content.markdown",
"text": "While having a good time with internet friends on Bluesky, I saw [a thread asking about Josh Comeau’s website](https://bsky.app/profile/philanelson.bsky.social/post/3lafk37shyc2l), specifically asking about why the entire card isn’t clickable. Josh’s replies concerning usability is spot on, and I’ve definitely been naughty doing the typical `display: block` on a `<a/>` element right here on the blog homepage. The replies in the thread also have some good suggestions but I was wondering if there was alternative. I have two main criteria:\n\n- Use HTML & CSS only, so there’s no need to listen for additional behavior.\n- Ensure that it meets user expectations both in terms of accessibility and also visually.\n\nI think I have something…\n\n## Forgotten memories\n\nIn the list of HTML tags that people forget about, there’s two that are very often unused: `<map/>` and `<area/>`. These are meant to be used with an associated `<img/>` so a user can click defined regions of an image. [The example for `<area/>` on MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area) has a good sample of this.\n\nThe idea is that you define the areas’ shapes using some attributes and then group them into the `<map/>`. Then you assign that to the `<img/>` using `usemap`, which is a reference to the `<map/>`. Each `<area/>` can accept many of the same attributes of an `<a/>`, most importantly including `href` and `target`.\n\nSo what if we could include a visually hidden image the size of the card and then add an `href` to the entire area of the image? Let’s take a look:\n\n## Sourcery\n\nWe certainly don’t want to make a request for an image that is only being used to support this behavior. We’d want some resource that was quick and hard-coded. Stack Overflow has a topic on “[smallest filesize for transparent single pixel image](https://stackoverflow.com/questions/2570633/smallest-filesize-for-transparent-single-pixel-image)” which is exactly what we need. I’ll add a few additional attributes as well.\n\n```\n<img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEAAAAALAAAAAABAAEAAAIBAAA=\"\n aria-hidden=\"true\"\n height=\"100%\"\n width=\"100%\"\n usemap=\"#name\"/>\n```\n\nThe `aria-hidden=\"true\"` is added because this image is strictly to support the area that we want to click, we don’t want to expose it in the accessibility tree. The `width` and `height` attributes are ensuring that the image is the full size of the container it is placed within. And the `usemap=\"#name\"` is meant to reference a `<map name=\"name\"/>` somewhere in the page.\n\nNext we’ll make the `<map/>` and `<area/>`:\n\n```\n<map name=\"card\">\n <area\n alt=\"Design Systems Hot Takes\"\n href=\"https://blog.damato.design\"\n shape=\"default\"\n target=\"_blank\"/>\n</map>\n```\n\nThe `href` and `target` should be clear. The `shape=\"default\"` tells the `<area/>` to be the same size as the `<img/>`. The `alt` is the same text you’d expect for images, except here we’re using it as the label for the link. On MDN, it mentions that this is used for browsers that don’t show images.\n\nFinally, let’s put all of this in a card:\n\n```\n<div class=\"card\">\n <div class=card-contents\">\n Card contents\n </div>\n <map name=\"card\">\n <area\n alt=\"Design Systems Hot Takes\"\n href=\"https://blog.damato.design\"\n shape=\"default\"\n target=\"_blank\"/>\n </map>\n <img src=\"data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEAAAAALAAAAAABAAEAAAIBAAA=\"\n aria-hidden=\"true\"\n height=\"100%\"\n width=\"100%\"\n usemap=\"#name\"/>\n</div>\n```\n\nNote that this could be optimized such that the `<img/>` is added *once* and all `<map/>` elements in every card can use the same `name` reference. Then the only unique element to each card would be the attributes in the `<area/>`. This means you’re only “rendering” a single image.\n\n## Styles\n\nNow for some styles, and there isn’t a lot so I’m going to write everything below and explain:\n\n```\n.card {\n display: grid;\n}\n\n.card:focus-within {\n outline: 5px auto Highlight;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\n.card > :not(map) {\n grid-area: 1 / 1 / -1 / -1;\n}\n\n.card > map {\n position: absolute;\n transform: scale(0);\n}\n```\n\nFirst, the grid styles are a different way of getting the direct children to fill the container. Traditionally we’d place `position: relative;` on the `.card` and then `position: absolute;` on the `img`. However, that could cause layering problems since `position` creates a new [stacking context](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index/Stacking_context). The grid properties here will make the children fill the container.\n\nNext, the `.card:focus-within` listens for when the `area` element is focused and applies a ring to identify this card is ready to be interacted with. You can add your own indication, I’m using the [default browser styles](https://css-tricks.com/copy-the-browsers-native-focus-styles/).\n\nFinally, the styles for `map` ensure that it doesn’t take up any space in the composition but is still reachable with assistive technologies. The styles within here are a new way of supporting [visually hidden](https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html) so the `alt` text is reachable.\n\nI have [a CodePen of the implementation](https://codepen.io/fauxserious/pen/OJKaOZj) below, including the traditional block anchor:\n\n<p>See the Pen <a href=\"https://codepen.io/fauxserious/pen/OJKaOZj\"> Clickable card!</a> by Donnie D’Amato (<a href=\"https://codepen.io/fauxserious\">@fauxserious</a>) on <a href=\"https://codepen.io\">CodePen</a>.</p>\n\n## Results\n\nSo now this should allow for all the normal link behavior; hovering to see the link destination, right-click to open in new window, etc.. This also allows for some more interesting options. Perhaps you’d want to allow for *parts* of the card to be clickable so that other text can be selectable, you could adjust the position of the `<img/>` to cover the area. Maybe you’d want to conditionally allow text in the card to be selectable. Use CSS to hide the `<img/>`.\n\nHave I tested anything else past what I’ve said here? Nope. But, maybe someone can be further inspired and continue down this path.\n\nIs this really any different from adding a display block `<a/>` and filling it inside the card like it is done with the `<img/>`? *Sort of*, since it would still need content for assistive technologies to read, and you’d need to wrap the content in a `<span/>` and visually hide it specifically for ATs, or maybe use an `aria-label`. Maybe it’s easier, but maybe there’s something to the approach above that makes it better in some way?\n\nAt the very least, I bet you probably learned about some HTML elements you haven’t heard of. 😉",
"version": "1.0"
},
"description": "Everyone does it, and we often do it poorly.",
"path": "/posts/clickable-cards",
"publishedAt": "2024-11-08T00:00:00.000Z",
"site": "https://blog.damato.design",
"tags": [
"css",
"accessibility",
"interactions"
],
"textContent": "While having a good time with internet friends on Bluesky, I saw a thread asking about Josh Comeau’s website, specifically asking about why the entire card isn’t clickable. Josh’s replies concerning usability is spot on, and I’ve definitely been naughty doing the typical on a element right here on the blog homepage. The replies in the thread also have some good suggestions but I was wondering if there was alternative. I have two main criteria:\nUse HTML & CSS only, so there’s no need to listen for additional behavior.\nEnsure that it meets user expectations both in terms of accessibility and also visually.\n\nI think I have something…\n\nForgotten memories\n\nIn the list of HTML tags that people forget about, there’s two that are very often unused: and . These are meant to be used with an associated so a user can click defined regions of an image. The example for on MDN has a good sample of this.\n\nThe idea is that you define the areas’ shapes using some attributes and then group them into the . Then you assign that to the using , which is a reference to the . Each can accept many of the same attributes of an , most importantly including and .\n\nSo what if we could include a visually hidden image the size of the card and then add an to the entire area of the image? Let’s take a look:\n\nSourcery\n\nWe certainly don’t want to make a request for an image that is only being used to support this behavior. We’d want some resource that was quick and hard-coded. Stack Overflow has a topic on “smallest filesize for transparent single pixel image” which is exactly what we need. I’ll add a few additional attributes as well.\n\nThe is added because this image is strictly to support the area that we want to click, we don’t want to expose it in the accessibility tree. The and attributes are ensuring that the image is the full size of the container it is placed within. And the is meant to reference a somewhere in the page.\n\nNext we’ll make the and :\n\nThe and should be clear. The tells the to be the same size as the . The is the same text you’d expect for images, except here we’re using it as the label for the link. On MDN, it mentions that this is used for browsers that don’t show images.\n\nFinally, let’s put all of this in a card:\n\nNote that this could be optimized such that the is added once and all elements in every card can use the same reference. Then the only unique element to each card would be the attributes in the . This means you’re only “rendering” a single image.\n\nStyles\n\nNow for some styles, and there isn’t a lot so I’m going to write everything below and explain:\n\nFirst, the grid styles are a different way of getting the direct children to fill the container. Traditionally we’d place on the and then on the . However, that could cause layering problems since creates a new stacking context. The grid properties here will make the children fill the container.\n\nNext, the listens for when the element is focused and applies a ring to identify this card is ready to be interacted with. You can add your own indication, I’m using the default browser styles.\n\nFinally, the styles for ensure that it doesn’t take up any space in the composition but is still reachable with assistive technologies. The styles within here are a new way of supporting visually hidden so the text is reachable.\n\nI have a CodePen of the implementation below, including the traditional block anchor:\n\nSee the Pen Clickable card! by Donnie D’Amato (@fauxserious) on CodePen.\n\nResults\n\nSo now this should allow for all the normal link behavior; hovering to see the link destination, right-click to open in new window, etc.. This also allows for some more interesting options. Perhaps you’d want to allow for parts of the card to be clickable so that other text can be selectable, you could adjust the position of the to cover the area. Maybe you’d want to conditionally allow text in the card to be selectable. Use CSS to hide the .\n\nHave I tested anything else past what I’ve said here? Nope. But, maybe someone can be further inspired and continue down this path.\n\nIs this really any different from adding a display block and filling it inside the card like it is done with the ? Sort of, since it would still need content for assistive technologies to read, and you’d need to wrap the content in a and visually hide it specifically for ATs, or maybe use an . Maybe it’s easier, but maybe there’s something to the approach above that makes it better in some way?\n\nAt the very least, I bet you probably learned about some HTML elements you haven’t heard of. 😉",
"title": "Clickable cards",
"updatedAt": "2026-06-13T16:19:08.174Z"
}