DEV Community

Tammy Shipps
Tammy Shipps

Posted on • Edited on

A Handy Snippet for Accessible Images in Craft CMS

Friends don't let friends code unaccessible images.

The backstory

We have a fairly sizable marketing website where I work, and recently we've been pushing toward making it accessibility-compliant (yay!) While cleaning up some of the older template structure of our Craft CMS-run website, I quickly realized I had a scaling problem (boo!)

There were tons of <img> in the templates, possibly hundreds, and only a few prominent image placements included things like an alt tag or aria label on the element - things that should be on every image on the site.

The trap: "It looks right so it must be right"

For a lot of developers who work on the front-end, a large portion of our job is "making it look right." That sounds easy, but in practice, it's anything but - especially with deadlines, last-minute design tweaks, copy changes, and global pandemics getting in the way.

Some developers (myself included) can get so bogged down with making it look pixel-perfect that they forget to make sure it works properly for edge and special cases, too.

I've been guilty of this plenty of times... Honestly, it's pretty easy to forget about the mess under the hood if the paint job looks perfect. But the writing is on the wall: Accessibility is important and can't be overlooked any longer. So how do we get better at these small tasks that have such a huge impact?

The solution

That's where DRY comes in! Yes, even for things like <img> tags.

In this case, since have a lot of images, and we want to make sure that every single one has the required element properties, it makes sense to use a template partial or macro.

First, I made a new file under templates named templates/_includes/utilities/image.twig to hold the new code:

{#
  Displays an image from a Craft image object in an accessible fashion.

  @param {object} image - the image data object
  @param {string} transformation - image transformation or transformation handle
  @param {string} classes - additional css classes to add
#}

{% set transform = transformation|default('') %}
{% set src = image.getUrl(transform)|default('') %}
{% set alt = image.altText|default('') %}
{% set title = image.title|default('') %}
{% set classes = classes|default('') %}
{% set height = image.getHeight(transform)|default('') %}
{% set width = image.getWidth(transform)|default('') %}

<img
  src="{{ src }}"
  alt="{{- alt -}}"
  title="{{- title -}}"
  class="{{ classes }} transformed-image {{ transform }}"
  id="{{ image.id }}"
  height="{{ height }}"
  width="{{ width }}"
/>

Note that we are using the Typed Link Field plugin for Craft CMS, so our link data object structure might look different than yours, but the concept is the same!

And then, to use the above to embed an image, I used the following within the templates:

{% set featuredImage = entry.featuredImage.one() %}

{% if featuredImage is defined %}
  {% include '_includes/utilities/image' with { image: featuredImage } %}
{% endif %}

And that will spit out a nicely formed <img> tag with all of the properties required, every time. Whew!

It's very versatile due to being able to optionally pass in one-off customizations like CSS classes and named transforms and the code will generate the correct end result for you.

...

And the next time I need to fix an image tag on the site, I can do it in one place and not have to hunt and peck through who-knows-how-many templates.


Edit: June 29, 2020

If you want to automate other a11y traits, such as aria-hidden on your images, you can extend the snippet slightly, like so:

<img
  src="{{ src }}"
  alt="{{- alt -}}"
  title="{{- title -}}"
  class="{{ classes }} transformed-image {{ transform }}"
  id="{{ image.id }}"
  height="{{ height }}"
  width="{{ width }}"
  aria-hidden="{{ alt|length > 0 ? 'false' : 'true' }}"
/>

This way, your image will automatically include `aria-hidden="true" when there is no alt text - essentially making it a display-only image. And of course, once you add the alt to the back-end in the Craft admin, it will change itself accordingly.

\o/

Top comments (0)