Bruce Lawson's personal site

Notes on draft CSS “alt” property

A while ago, there was discussion in CSS Working Group about an alt property to be added to CSS. Its purpose is to let assistive technology know the meaning of unicode characters inserted into a visual rendering of a document with CSS generated content. The problem is described by James Craig, Apple’s nice accessibility chappie.

It’s been shipping as -webkit-alt for a year now, and has just been added to the draft CSS Pseudo-Elements Module Level 4 spec. Let’s say you’re using a star glyph to show something is “new”, by generating CSS content using a class of .new as your selector:

.new::before { 
  content: url(./img/star.png);
  alt: "New!"; 

The alt gives information to assistive technology.

The alt string can be blank. Assume you’re generating “►” to show that a widget is expandable. Because you’re a lovely human being and a responsible developer, you’re using aria-expanded="false" in the HTML. Therefore, you’d use empty alt in the CSS that generates the glyph to prevent assistive technology “helpfully” reading out “Black right-pointing pointer” after the AT has relayed the ARIA information to the user:

.expandable::before {
  content: "\25BA"; /* a.k.a. ► */
  alt: ""; 
  /* aria-expanded="false" already in DOM, 
     so this pseudo-element is decorative */

Update 17:05 GMT fantasai of the CSS WG has mailed the CSS Working Group protesting against the syntax above. I don’t have any horse in the syntax race; my aim with this post is to show that we need some mechanism to give alternate content to assistive technology given that we allow non-textual content to be generated via CSS.

When I tweeted about this earlier, a few people objected to the concept because it adds content to CSS. But that ship has long sailed, with the content property that people have been using for ages. If you say “adding non-decorative content to a page is a misuse of CSS that merits the author 100,000 years in purgatory with Tim Berners-Mephistopholee pouring hot Ovaltine over their dingle-dangles whenever they try to sleep”, I will vehemently nod my agreement.

But sometimes, for organisational reasons, or because you inherited code, or you’re refactoring code from before you saw the light, you’re working with CSS that generates icons. This new property at least allows an author to make that content more accessible.

Years ago, I joined a web team a week after their brand new website had been delivered, months late and squillions over budget, by Big London Agency PLC. The site was a vile splodge of nested layout table cells and spacer GIFs with “click here” links pointing to PDFs. Of course, I could have instantly resigned my job and let the bank repossess my house.

Instead, I gently pointed out to any who would listen the inadequacies of the site and made plans about how it could be refactored. But while biding my time, I was also making small tweaks here and there to make it better and incrementally more accessible; removing duplicate link text from title attributes, changing “click here”s, putting blank alt text on spacer GIFs in the small parts of the template I controlled.

This was before the days of ARIA but I would have jumped at the chance of adding role="button" to the horrible mess of nested <div>s that the CMS spat out. Yes, it’s much better to spend a fortune getting the external CMS provider in to change the templates, the retest every single form on every single browser – but that simply wasn’t possible.

Two years later, when the senior manager who commissioned the terrible site left and we could jettison it, we did. And then we did it right. But for those who needed the content and were unable to wait nearly three years (that is, everyone), I’m glad I applied sticking plasters and polished turds.

It’s often said that if a job’s worth doing, it’s worth doing well. But it’s also wise to remember: sometimes, if a job’s worth doing, it’s worth doing badly than not at all. If you must have meaningful content generated by CSS, at least now you can make it accessible.

Update 1 May 2020: The syntax has now changed. Ian Lloyd tweeted

.warning-with-alt:before {
 content:"! " / "Warning ";
<p class="warning-with-alt">Be careful

It announced “Warning Be careful in VO/Chrome combo. All other browsers I tried on Mac ignored the whole line, so no pseudo content added. Andy Ronksley wrote

The alt text is added to the content property now (after the forward slash). Last time I used this a few years back, Safari only supported the older syntax. Sounds like Ian has confirmed it still doesn’t support the new syntax.

(Last Updated on )

Buy "Calling For The Moon", my debut album of songs I wrote while living in Thailand, India, Turkey. (Only £2, on Bandcamp.)

14 Responses to “ Notes on draft CSS “alt” property ”

Comment by fantasai

I’m not convinced that this is a good idea. The ‘content’ property in CSS3 will be able to take lists, and we have @media for switching between media if that’s also necessary.

Comment by Bruce

I’m not wedded to the syntax of it, fantasai – my aim is to show that we do need some method of providing alternate content to AT in CSS, given that we allow non-textual generated content. Updating the article to link to your WG mail.

Comment by Ian

Perhaps alt could act like content and accept the value of an attribute, thus putting the content back in the markup and away from CSS.

For example:

div class=”new” data-alt=”new”


.new:before {
content: url('./img/star.png');
alt: attr(data-alt);

Bruce notes: Ian’s code got mangled by WP commenting system; blame it (and me), not him!

Comment by Bruce


sorry my commenting system mangled your code.

AFAIK, James Craig proposes such a way. But there’s argy-bargy at the WG about the syntax.

Comment by Le Dahu Lévogyre

With Ian’s solution, the AT still has to read the CSS. Why ?
Can we not add a new alt attribute on everything, and nothing required in the CSS ?

div class=”new” alt=”new”

As far as I understand it, what we’re trying to do here is to make the AT read out a semantic attribute of the element. Right ?

Comment by Alex Bondarev

Fully agree with the idea. As this property will potentially be used only with generated content, why not generate it in a bit more descriptive manner? You “New!” example is great.

Comment by goetsu


you say : “we need some mechanism to give alternate content to assistive technology given that we allow non-textual content to be generated via CSS”

I’m not sure of that I think what we need is that the spec explicitly state that the content must be decorative only and not expose by the browser to the accessibility API

Comment by Bart Simons

@Thierry, I don’t understand your comment. CSS generated content does sometimes convey information so it should be exposed to assistive technology.

From our tests we concluded that all browsers except Internet Explorer pass on the contents of the content property to screenreaders.

@Bruce, please be careful when mentioning ARIA. It is not as simple as “adding role=”button” to the horrible mess of nested s”. See

Comment by Thierry


What I’m saying – and it seems @Goetsu thinks the same – is that the specs should have never mandated screen-readers to speak CSS generated content. That’s the presentational layer and has nothing to do with content per se, the kind of content text-browsers and some other UAs can parse. Content that actually belongs to the HTML document.

In my opinion we went this route because we were afraid that authors would abuse this and ignore some users [1], but that was wrong. It would have been better to discuss the proper usage of CSS generated content.


Comment by Kazuhito

I’m just curious whether there is any way to specify language of alt property content, like lang attribute of HTML, so that AT can read aloud the content more naturally. Do browsers / ATs recognize the content as it’s written in the same language specified in HTML?

Comment by Bruce

@BartSimons said “please be careful when mentioning ARIA. It is not as simple as “adding role=”button” to the horrible mess of nested divs” – well, I didn’t say it was that simple (you need to ensure the divs can be focussable by the keyboard, for example). You give a link to the top of a long, dull document – which particular section are you pointing people towards? That would help people wade through it.

@Thierry said “The real problem in my opinion is that CSS generated content should have never been exposed to Assistive Technology”

i don’t see how anyone gains if content is shown to visual users but hidden from disabled users.

@goetsu says “what we need is that the spec explicitly state that the content must be decorative only “

I think you over-estimate the power of informative text in specs, and the number of developers who read specs.

My point is that (for whatever reason, for better or for worse) we have a situation in which authors put meaningful content in the CSS. So we need a way to make that accessible. “Go away and do it again properly, refactoring your website and CMS to produce that content in HTML” is not the way to achieve that.

@kazuhito not as far as I know; similarly, there’s no way of specifying the language of the alt attribute on HTML img.

Comment by Artur Ortega

Imagine you use the CSS icon of the example in this posting but you have a section in German and a section in English. You would be able to provide the alt attribute accordingly just by using the language of the element it applies to:

.new::before {
content: url(./img/star.png);

.new:lang(en) {
alt: “New)”;

.new:lang(de) {
alt: “Neu)”;

This makes it possible to use the same icon but language specific ALT attributes for the same icon on different elements depending on their language:
<i lang=”en” class=”new”/>
<i lang=”de” class=”new”/>

Comment by Zoltan Hawryluk

I read about the new syntax today from this great blog post:

After reading it, I remembered this article from a few year back, and it gave me inspiration to look into this further. This helps cross browser issues (so far):

@supports (content: "x" / "y") {
.new-item::before {
content: "★" / "Highlighted Text:";

@supports not (content: "x" / "y") {
.new-item::before {
content: "★";
alt: "Highlighted Text:";

Interestingly enough, the Safari-only alt property also works with var(), which could help from an i18n/l10n perspective (an idea given by Andrea Giammarchi in the blog post above):

I need to do more testing here, but it I am right, the only thing you would have to worry about here now is Firefox (and IE .. but that is going to be a non-issue soon, I hope).

Hope this helps drive further research into this.

Leave a Reply

HTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> . To display code, manually escape it.