CSS, when used properly, can make your HTML better!
In this mini series I will be using CSS to enforce using the correct HTML elements, required attributes for accessibility etc. within our HTML.
There are two aspects to this.
The first is that we structure our CSS so that using the wrong element or attributes is impossible and styles will not work if your HTML is wrong!
The second part of this is that we add a stylesheet to our projects in development mode to help pick up on errors quickly, and then remove it for production.
As an easy introduction to this concept we will utilise the second method (a development error checker stylesheet) and explore how we can use CSS to ensure that we always remember our alt attributes (but also allow for decorative images where alt attributes should be left blank!)
1. Identifying missing alt attributes.
There are two things we are going to use as a first step.
First we need to identify any images where the alt
attribute is entirely missing.
img:not([alt])
What this effectively says is:
-
img
- Select all images -
:not(
- Where the following is not applicable -
[alt]
- there is not an attribute (attribute is denoted by square braces[]
) with the name ofalt
(the bit within the square braces is the attribute we are selecting).
This now let's us apply styling to any images (img
) that do not (:not
) have an attribute ([attribute name]
) of alt
.
The other issue we need to identify is when the alt attribute is present, but someone has not filled it in.
img[alt=""]
What this effectively says is:
-
img
- Select all images -
[]
- with the attribute -
alt
- alt attribute -
=""
- is not filled in (blank).
The final part will also pick up on alt attributes that are null so it will match alt
and alt=""
.
Example
Content Management System (CMS) defaults
In the above example you may have noticed an issue.
Some CMSs add some default alt text that should be replaced.
<img alt="Alt Text">
for example.
With our current solution these would pass our test, even though that alt text is completely useless!
Now you would have to adjust this for your CMS, but the principle is straight forward:
img[alt="Alt Text"]
We just look for any img
element that has the exact alt text of "Alt Text" (which you would replace with the default output of your CMS).
But what about decorative images?
While our empty alt text check may be a good start we do occasionally want to leave the alt
attribute empty on purpose.
By adding an empty alt attribute we are signalling to Assistive Technology, such as a screen reader, that "this image is decorative and should not be announced".
So how can we identify these images in our HTML?
Enter role="presentation none"
If you are newer to HTML you may not have learned about WAI-ARIA yet.
WAI-ARIA is a way that we can provide additional information to assistive technology so that it makes the page make more sense.
A prime example would be a screen reader. This is a piece of software that converts your HTML into output (usually spoken text) so someone who is blind (for example) can still use your website.
This is what your alt
attribute is for, describing an image to assistive tech and people who have images switched off.
But as I mentioned earlier, sometimes we have an image that adds no value to the page (a background image for example) that we do not want announced.
While an empty alt
attribute will ensure it is not announced I find it a good practice to add role="presentation"
to the img
to ensure the image is not announced.
Let me explain a little: a lot of elements have semantic meaning (an <img>
element is announced as an image, a <section>
element is announced as a section so different parts of the page make sense to Screen readers etc.).
role="presentation"
tells assistive tech "hey, this element has no semantic meaning or significance, treat it like a <div>
" and so it does not announce the element type etc.
So it adds an extra layer of safety as if someone accidentally added some text within the alt
attribute on a decorative image it will still not announce anything (as you cannot have an alt
attribute on a <div>
essentially).
Additionally there is an improvement to WAI-ARIA where you should add role="none"
in the future instead of role="presentation"
as it is far more clear that you are removing the semantic information by saying "none" instead of "presentation" (they do the same thing though, they are synonyms).
Unfortunately support is not great for role="none"
yet and we are at a change over point, so the recommended advice is to use role="presentation none"
for maximum compatibility and to future proof your HTML at the moment.
Bringing it all together
So we need a solution that:
- finds missing alt attributes
- finds empty alt attributes
- finds alt attributes with a certain value ("Alt Text" in our example)
- ignores missing alt attributes on items with a
role
containing a combination of "presentation" and or "none".
Final CSS selector and example
Out final CSS selector looks like this:
img:not([alt]),
img[alt="Alt Text"],
img[alt=""]:not([role="none presentation"]):not([role="presentation"])
So we have our first selector to check the alt
attribute exists on an image (as it always should even if it is null or empty!).
Then we have a second check (separated by a comma to denote a new selector) for our CMS output to remind us to change it.
Then we have our third check which will select anything with an empty alt attribute, provided it does not have a role of "presentation" or a role of "presentation none".
Note that this will still fail on role="none"
as support is not great and role="none presentation"
as this doesn't actually fall back to presentation and would not work in assistive tech that does not support role="none"
.
This is exactly what we need!
An Example
In the following CodePen I have set up a few tests with their expected states above them. As you can see our selector works as expected! yey!
Can we go further?
Yes, of course we can!
We can also pick up bad practices with the actual content of the alt attributes!
What do I mean?
Well if you write alt descriptions that start "image of" or "picture of" they are not useful and can be very annoying for assistive tech users (remember that they already know it is an image because we used an <img>
element that announces itself as an image!).
CSS has a limited form of regular expressions we can use to achieve this!
We can check for "starts with" using ^=
.
So we can use an attribute selector like:
img[alt^="Image of"]
To find all alt attributes ([alt
) that start with (^=
) the text "Image of" ("Image of"
).
However this has a problem!
If we had "image of" it would not match as selectors are case sensitive by default and "Image of" is not the same as "image of" as far as the selector is concerned!.
Luckily we have one more trick up our sleeve!
We can add " i" to our attribute selector to make it case insensitive (notice the space before the letter i!)!
So our final selector becomes:
img[alt^="image of" i],
img[alt^="picture of" i]
And now we can highlight any items with poor alt attributes too! (obviously there are more examples of poor phrasing that those shown here!)
Final example
Here is the final example where we have added two more tests to the bottom that should show in an orange dotted outline due to the alt text starting with "image of" and "picture of".
Checking for other attributes!
If you haven't heard about Core Web Vitals yet, they are 3 metrics used by Google to help assess the quality / speed of your page.
One of these is called Cumulative Layout Shift.
In essence it measures how much the page "jumps around" while loading, which can annoy users and result in unintended clicks etc.
One big cause of layout shifts is images.
To avoid layout shifts modern browsers can allocate the space an image with take up on a page before it has loaded, provided you either use aspect-ratio
in your CSS or give your image a width
and height
attribute (which is generally better if you have multiple image shapes and sizes).
So to check our image has width and height attributes we can use:
img:not([width]),
img:not([height])
This is the same principle as checking for an entirely missing alt
attribute we did earlier.
I would add this as a "warning" unless you specifically state that you want all images to have a width and height attribute in your internal development guide!
longdesc
A big thanks to Alvaro for reminding me of this attribute, go check out their stuff it is awesome!
Checked out Alvaro's stuff? Great, let's get back to it!
One other thing we can do is check for deprecated / poorly supported attributes such as longdesc
This time we want to do the reverse of our previous checks for whether an attribute is missing (we want to see if it exists to throw an error).
So we can use:
img[longdesc]
and add a warning (as the longdesc
won't do any harm provided you have a good alt
description already).
Conclusion
This style sheet is a useful way of enforcing image best practices.
As with anything this is not without flaws.
It doesn't work for people who have vision impairments for example.
However it is good as part of your development toolkit if you do not have vision impairments and want a quick and dirty way to see if you have remembered your alt attributes / done them properly!
In the next part of this series we will cover using CSS to make sure that poor HTML habits do not continue (such as using <div role="button"
) by making it that only the correct element will allow styling to be applied to it!
Thanks for Reading!
Thanks for reading, I hope you enjoyed it!
In fact to show my appreciation please do have a virtual ❤ and 🦄 to show how much it means to me!
I hope you enjoy the rest of your weekend!
Top comments (18)
Any other tricks you can think of to do with images and accessibility?
One thing i didn't cover was that to avoid Layout Shifts that contribute to CLS you should have a
width
andheight
attribute on an<img>
.The selector is the same principle as that for a missing
alt
attribute,img:not([width])
andimg:not([height])
in case you were wondering!I hope you are all having a great weekend and you found this article useful. ❤🦄❗
I might have mentioned Checka11y.css in the past. It has different tricks for this.
One more that you could add is
longdesc
. The attribute is deprecated and poses some accessibility concerns/challenges (warning). And if it points to an image or contains text, it is incorrect as it should have a link to a page (error.)Great suggestion on
longdesc
, I will add that in tomorrow as part of the article as I had forgotten about that (just about to go out otherwise would do it now!)!In fact I literally just saw this github.com/jackdomleo7/Checka11y.c... now that you mentioned it on the checka11y github!
I will be introducing checka11y later in the series after you pointed me to it a few weeks ago! ❤
I'm adding a bunch of tickets to do in that repo (pending acceptance). It is a fun project in case you want to collaborate. 😉
This isn't a pretty new idea, there are a few CSS stylesheets out there which highlight these errors. The first one which comes to mind is the a11y.css bookmarklet.
Maybe you could compile all the a11y errors into a bookmarklet?
You are right, but the new parts in this are the enforced use of
role=“presentation none”
for decorative images and catching default output, neither of which I have seen before (and won’t see as they are not things a generic library could do).The idea is to show how you can enforce company wide policies etc.
However I will be introducing that project later in this series as they have some great tricks etc! 👍❤️
Nice one. Reminds me of using
border: 1px solid red
as a debug tool.This could be nice in a separate stylesheet for development environment only. Don't want to broadcast I'm an idiot on my site 😁
Oh very much a development only stylesheet!
I personally have it linked to my environment settings so I can't accidentally include it in production!
However the second part of this system is for production and development as it just stops developers doing things wrong!
For a simple example:
<div class="btn-large"
just won't work as the selector is forbutton.btn-large, a.btn-large
!More on that in the next post! 😉❤
It's perfectly valid for an image to have an empty
alt
attribute, but some less experienced devs might not understand and be tempted to push unnecessary content into the attribute, making accessibility worse in some cases. The image shouldn't require the role, and the spec is clear on this that an image with emptyalt
text should not be part of the accessibility tree.I said that it isn’t required within the article, it is a way of signalling that the alt attribute should indeed be empty to developers (and to protect against developers adding content unnecessarily). Certainly not required but something I like to do to help avoid a mess in the future.
Gracias
Es el primer post que veo de DEV.
Creo que esta web va a ser un descubrimiento muy interesante.
Glad you enjoyed it and as this is your first post you have read, may I say "welcome to DEV" on behalf of the community!
I hope you enjoy it here!
Nice article, I never thought about checking for tag properties with css to check if images were accessible, thanks for another great accessibility article.
Thanks a lot, glad you enjoyed it and hope you find it useful! ❤️
This is such a helpful post. I'll be making use of what I learned for sure!
Thanks, glad you enjoyed it!
Great read.
Thanks Andrew! ❤️