A Handy Snippet for Accessible Images in Craft CMS
Tags: webdev, a11y, twig, craft
Published: 2020-05-04 22:27:53

---
title: A Handy Snippet for Accessible Images in Craft CMS
published: true
description: DRY up your Craft CMS templates with a re-usable include that makes sure your images are always embedded properly
tags: webdev, a11y, twig, craft
---

##### 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](https://www.w3.org/WAI/standards-guidelines/wcag/) (yay!) While cleaning up some of the older template structure of our [Craft CMS](https://docs.craftcms.com/v3/dev/)-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](https://www.forbes.com/sites/ryanrobinson/2019/09/25/website-accessibility-online-business/#23892b079c19): 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](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) 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](https://symfony.com/blog/simpler-macros-in-twig-templates).

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

```twig
{#
  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:

```twig
{% 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](https://docs.craftcms.com/v3/image-transforms.html) 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:

```twig
<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/