Musing Upon an [alt] Text Badge on Images

The Mastodon instance I’m on has a little custom CSS applied to it which shows a visual [alt] badge on images that have an alt attribute:

Screenshot of a Mastodon post with an image with a proper [alt] badge.

And as a little light shaming, a [no alt] badge on images that don’t:

Screenshot of a Mastodon post with four thumnail images where all four of them have a [no alt] badge. This is super meta alt text to be writing.

Sorry about mine there. It appears there is some kind of bug with the Share on Mastodon plugin for WordPress that prevents my alt text from making the leap.

Eric Meyer blogged about how he did his own with a user stylesheet. And here’s another blog post about it. User stylesheets are pretty great. It surprises me that of the Big Three browsers, Safari makes them easiest to add. Yadda yadda security. Me, I’m super partial to “Boosts” in Arc.

It’s a wonderful use of CSS’ :has() selector. These badges need to be “inserted” into the site. There is no HTML present to hide/show. That’s pseudo-element territory. But you can’t put a pseudo-element on an image!

Well, you can but it won’t show up.

So you need to put it on another element. Ideally, a wrapper element, if there is one! Fortunately, on Mastodon there are several:

You can see the ::after element in pink above. So how is that added based on a conditional situation on elements within it? That’s what I mean by :has().

.media-gallery__item:has(img[alt])::after {
  content: "alt";
}Code language: CSS (css)

I don’t really know what we would have tried without that CSS feature. Probably a smidge of JavaScript to insert an element where it needed to be. Or a lucky combinator selector if placing the pseudo-element on a next-sibling was consistent enough to work.


This also makes me think about potentially displaying that alt text too.

No dice there though, because again we can’t put useful pseudo-elements on <img>. So even though we can select against the [alt] and style a parent element, we can’t pluck out the content of the alt and use it in content elsewhere.

/* Not helpful. */
img[alt]::after {
  content: attr(alt);
}

/* Not possible */
div:has(img[alt])::after {
  content: attr-from-a-child-element(alt);
}Code language: CSS (css)

I think the best we can do here (if this matters at all, which, I’m not it does, alt text isn’t a figcaption) is to duplicate the alt text in another attribute on a parent.

<div data-alt="foo">
  <img src="..." alt="foo">
<div>Code language: HTML, XML (xml)

Then we can use that attribute to visually show (on hover here, just for fun):

div {
  width: min-content;
  position: relative;
}
div[data-alt]::after {
  content: attr(data-alt);
  opacity: 0;
  visibility: hidden;
  position: absolute;
  bottom: 2px;
  left: 2px;
  width: calc(100% - 4px);
  background: rgb(255 255 255 / 0.85);
  padding: 1rem;
  transition: 0.1s;
}
div[data-alt]:hover::after {
  opacity: 1;
  visibility: visible;
}Code language: CSS (css)

But let’s say we wanted to position the “popup” somewhere else? That’s perfectly possible.

But it’s fraught! What if that was along the top edge of the browser? That would cause “data loss” in that part of the text could be hidden with no way to access it. There is a possible solution on the horizon though. As Dave blogged in his CSS wishlist for 2023, the Popover API would be pretty sweet for this kind of thing, semantically and functionally (using actual HTML is ideal so you can put, like, links inside and stuff), and then would need to be combined with the CSS Anchored Positioning stuff to prevent the data loss.


CodePen

I work on CodePen! I'd highly suggest you have a PRO account on CodePen, as it buys you private Pens, media uploads, realtime collaboration, and more.

Get CodePen Pro

One response to “Musing Upon an [alt] Text Badge on Images”

  1. I like to prefix my custom styles with something like…

    :has(meta[property=”og:title”][content=”Mastodon”])

    …to prevent these stling to be applied to unwanted pages.

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to Top ⬆️