Jump to main content

"Fixing" Lists

  • Published:
  • Category: accessibility, Webkit, CSS

In September of 2017 Gerard K. Cohen of Unfettered Thoughts posted an article on the how VoiceOver and Safari (Webkit) (macOS and iOS) remove list element semantics when list-style: none is used. And it’s not just the use of list-style: none, but any CSS that would remove the bullet or number indicators of a list’s items will also remove the semantics.

This isn’t the only instance of CSS modifying how elements are interpreted by assistive technologies, such as screen readers. Modifying display and visibility to “none” or “hidden”, respectively, will not only visually hide content, but remove the elements from being discovered by screen readers. However, where as those properties will consistently hide and show content across browsers and assistive technologies, this particular example of changing the list style is unique to Safari.

Why is Webkit the only browser engine that behaves this way?

When a bug is a feature is a bug?

In March of 2017, a bug was filed about list-style: none affecting list semantics. Eventually, it prompted the response:

This was a purposeful change due to rampant “list”-itis by web developers. … Basically, if you remove all default visible indication of the list, there is no indication to a sighted user or screen reader user that the content is a list. If you want to override this heuristic for accessibility, you can always add an explicit ARIA role=”list”.

I can understand this reasoning. Semantics are hard and people do misuse HTML kinda a lot. The issue that can arise though are legitimate scenarios where one might want to remove the default styling of a list, but retain its semantics. Especially if that list is restyled in a way where it still visually looks like a list.

Not only that, but the idea that one should be expected to reattach list semantics with ARIA goes against the first rule of Using ARIA, which states:

If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.

Another excerpt from Using ARIA:

None of the elements defined in HTML4 need ARIA roles added to expose their default semantics. … In the majority of cases setting an ARIA role and/or aria-* attribute that matches the default implicit ARIA semantics is unnecessary and not recommended as these properties are already set by the browser.

It’s a constant struggle to educate developers who are new to accessibility for the reasons why “you don’t need to use ARIA if you use the correct HTML elements.” Only to then have to explain with your next breath:

“You need to use ARIA to add list semantics for this one specific browser and screen reader combination.

No you don’t need to do this for other browsers and screen reader combos.

No, this doesn’t happen with Chrome (Blink) and VoiceOver.

No, you’ll just have to ignore the warnings your automated checkers give you that say ‘don’t double up ARIA roles on their native element equivalents’.

No, this isn’t for Internet Explorer…”

When you give a developer an ARIA role

A bit of an aside here, but I think it’s important to emphasize that when you have to give the advice to use ARIA, it sticks. People who aren’t well versed in the ins and outs of using ARIA “learn” that it “makes content more accessible” and then it gets used in instances where it shouldn’t be necessary.

I previously wrote about ARIA lists, noting the hoops one has to jump through to create an unordered or ordered list with ARIA and CSS counters. In 2016 Marcy Sutton wrote her piece about Links vs Buttons which will likely continue to be relevant for years to come. To avoid a quirk they don’t understand, people will take on gobs of extra work to recreate elements with ARIA. Let’s not go down that path here please.

Another interesting bit about using ARIA, if you view the ARIA lists article in Firefox’s reader mode, the example I have of the ARIA list vanishes.

Why? It’s just a list? Isn’t it? hmm…

I’ve been able to inconsistently find this behavior with other ARIA based markup patterns, in some browser’s reader modes. Sometimes the ARIA patterns disappear, sometimes they remain, but these quirks don’t seem to happen when using standard HTML elements without ARIA modifications. Just something else people should think about when testing their content.

OK, back to talking about Webkit and VoiceOver…

A fix for the fix

Fortunately, ARIA isn’t the only way to re-add list semantics. The Unfettered Thoughts article provided a work around by noting that adding a zero-width space, using the list item’s ::before pseudo element, would re-introduce the list’s semantics.

Here’s the original code snippet with a single noted modification:

.list li {
  list-style-type: none; /* remove bullets */
}
 
.list li:before {
  content: "\200B"; /* add zero-width space */
  position: absolute; /* addition */
}

When using this snippet in the wild, I’ve found adding position: absolute to the pseudo element to be useful. There were instances in projects I’ve worked on where the zero-width space didn’t really act like a zero-width space. Instead weird gaps or unintended line breaks would be created by the hack. These visual quirks were largely due to other styles that were being inherited into the :before pseudo element by pre-existing styles that were still needed. By adding position: absolute, the pseudo element is removed from affecting the flow of the DOM’s content, and mitigates this potential issue.

Another caveat to using this solution is if your design was already using the :before pseudo element for other purposes, and adding the non-breaking space interfered with those styles. Some re-working of styles and markup could correct for this, but the effort there should be weighed against your Safari analytics.

As Adrian Roselli notes on Twitter a lack of list semantics “…may not be a big deal unless user testing says you really need a list.” While this behavior can be unwelcome in some situations, let’s also not spend too much effort over correcting an over correction which was in response to an over use of unnecessary semantics. Perform testing with real people!

A new alternative

An alternative to the Unfettered Thoughts CSS solution was made by Jonathan Neal. As documented in Jonathan’s PostCSS List Style Safari Fix, he found that using the following CSS would negate the need for the pseudo-element, but would re-introduce a list’s semantics to Safari and VoiceOver:

/* a stripped down version of Jonathan's selectors */
ol, 
ul {
  list-style: none;
}

ol, 
ul {
  list-style: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E");
}

/*
  I found that a url(#!) would also work for reintroducing the necessary semantics, but it would need more testing to ensure it didn't have any adverse affects in other browsers.
 */

Largely, this looks promising. In testing it appears to announce correctly with Webkit and VoiceOver, doesn’t interfere with other screen readers (tested with NVDA 2018.4 and latest JAWS 2019), and in reduced test cases appears to render well across most modern browsers.

The only issue I had with this technique was in trying it out on my website’s navigation (I presently don’t use either hack, and let the heuristics do what they will for my navigation).

By adding the list-style: url(...) to my existing navigation list selector, a gap appeared at the top of each list item in the navigation in Internet Explorer 11 and Edge 42:

screen shot of my website nav with new technique turned on, as displayed in Edge's dev tools. A gap appears at the top of each list item.

Turning the list-style off, the gap disappears and the navigation renders as expected.

screen shot of my website nav with new technique turned off, as displayed in Edge's dev tools. The gap goes away for each list item.

I’m guessing there’s something else in my CSS at play here that’s causing the gaps to occur. When trying this technique out in much simpler, but still similar rule sets, the gap doesn’t occur.

Wrapping up

I find myself a bit torn here. The decision to remove list semantics if a list is no longer styled to look like a list makes sense to me. Typically I think it’s important for elements to look like what they are. But a blanket decision like this can get into some opinionated territory. It also discounts the fact that there are other ways to visually interpret lists than just if they have default bullets. Hence that while I understand the decision, I don’t think Safari’s behavior here is correct.

It goes too far in the other direction, and it breaks form with how other browsers expose, and screen readers interpret, styled lists. Developers don’t typically expect CSS to affect the semantics of HTML. That was sort of the point of removing elements like <center>, <big>, <font>, etc. HTML is for semantics, and CSS is for styling.

But as I talked about in my article Unbuttoning Buttons, and Adrian Roselli has also in articles like Display: Contents Is Not a CSS Reset and Tables, CSS Display Properties, and ARIA, CSS can have some damaging affects on semantics, and developers can be none the wiser to them.

Just check out the many responses, retweets and likes to Sara Soueidan’s tweet:

There were many front-end devs and even those who are focused specifically on accessibility who unaware of this behavior in Safari.

As Eric Eggert exclaims:

and as James Craig responds to the above mentioned thread:

So, feedback is welcome :)