Which CSS properties affect the accessibility tree?

Viewed 691

By the sacred separation of contents and presentation, I assumed that CSS was ignored by screen readers. This naive assumption is false.

  • It appears that many ways of hiding contents from CSS, such as display:none; or visibility:hidden; or even width:0; height:0;, remove the hidden element from the accessibility tree. Their contents are not focusable e.g. via the tab key, and invisible to screen readers.

    [ Aside: Accessibility often requires hiding elements while keeping them accessible to screen readers: e.g. for skip links, alternative text, or a show/hide mechanism which only makes sense when using a screen. To actually do that, you need CSS trickery, such as moving the element off-screen by a large margin. Apparently these are commonly agreed methods, used and advocated by WebAIM and MDN. Also look at the CSS of the skip link on the result page of Google Search, for instance. ]

  • I also found that text added via the content property, often used to attach decorative characters ::before or ::after an element, does appear in the accessibility tree (to me, Firefox shows it as “static text”).

    [ Aside: This is often undesired as the added text consists of meaningless characters which happen to render as visually nice symbols, with the adequate font. ]

My question is in the title: which CSS properties (or values) affect the accessibility tree?

Of course CSS, as it governs presentation, matters for most visual impairments (dyslexia, myopia, all kinds of color blindness… or for anyone really). But I did not expect that CSS also matters for blind users. I thought that only the semantic contents given by HTML was important to them, and that the accessibility tree was built solely from HTML.

3 Answers

Properties that matter:

Visibility

CSS regarding visibility is picked up by screen readers as it is assumed you do not want people interacting with this item if it is not visible. Otherwise an entire section that is hidden from "sighted users" would be visible to screen reader users and offer them a different experience. For example:

  • display: none - universal support, if an element has this property it is assumed you do not want anyone seeing it.
  • visibility: hidden - same but some really older screen readers may pick this up still
  • opacity: 0 - not as effective as some screen readers may still pick this up but most will ignore.
  • visibility: collapse - use on tables, not sure how well supported it is but it should be interpreted as visibility: hidden by the browser (and therefore by the accessibility tree)

Size

If something is 0px tall or 0px wide it is not visible. For this reason the same rule is applied that if "sighted users" can't see it then screen reader users do not want to see it either.

Content property

This one is really inconsistent across different browser and screen reader combinations. You may see it in the accessibility tree but it does not mean that it will be used by the screen reader.

The thing is that technically screen readers should read the content.

Now because we as developers have used this for loads of styling tricks they have now improved the content property to have alt text. It only has 70% browser support at the moment and screen reader support will be even worse but eventually that problem will fix itself.

To get ahead of the curve on this all you need to do is add a slash between the displayed content and the alt text:

.myClass:after{
    content: "❤" / "favourited item";
}

CSS positioning

Technically this is kind of the opposite, where CSS is used to change the visual design (and make it logically ordered) but the DOM order is incorrect.

So although this one isn't quite a CSS affecting the accessibility tree item, it is closely related in such a way I thought I would mention it.

If you use CSS grid and change the positions of grid items you may end up with the DOM order being 1,2,3 but the visual order being 2,1,3.

Now if the logical reading order is 2,1,3 that can be super confusing for screen reader users as everything is read in the wrong order.

The same principle applies to float: right and flex-direction: reverse.

Negative margins can also cause some issues. For example be careful with margins when trying to visually hide text (when you mentioned about screen reader only text) - some browsers actually tried to correct for negative margins and meant that things were read out of order.

I would suggest reading this answer if you are looking for the current best practice on visually hiding things for screen reader users only!

Conclusion

I think that is it, but there could be others I haven't thought of.

In essence the reason CSS "leaks" into HTML here is because of how CSS affects what content is on the page and one core idea behind accessibility is "provide as similar experience / information as possible to people using assistive technology".

Surprisingly things like transform, backface-visibility: hidden; etc. do not seem to have any effect on screen readers, but I couldn't find anything in the spec that says whether they should or they shouldn't affect the accessibility tree and my tests were limited!

Finally I put "sighted users" in quotes as a lot of screen reader users have reasonable vision, some have perfect vision and use one to aid comprehension, it is just easier to compare "sighted" and "non sighted" when explaining the principles.

It's worth noting these behaviors are highly dependent on browser implementation. They will not be true for every browser / screen-reader combination. Screen reader behavior can vary widely between products.


  • The background-image property: when given an image URL is not visible to screen readers, as there is no way to add alt-text.

  • The clip-path property: can be used to hide content from sighted users. Does not remove content from the accessibility tree. The element is still accessible to screen readers and keyboard focus.

  • The content property: when used with a pseudo-element selector can be used to inject text-only content into the accessibility tree.

  • The display property: when set to none removes the element from the accessibility tree. It is hidden for sighted and non-sighted visitors.

  • The display property: when set to flex, block, or grid and applied to a <table> element, may force the table to be interpreted as a layout table, rather than a data table.

  • The font-size property: when set to 0 will hide text from sighted visitors, but it will still be accessible to screen readers.

  • The height and/or width properties: when set to a value of 0 is hidden for sighted and non-sighted visitors. When these properties are set to a value > 0, the areas not displayed will only be hidden from sighted visitors -- they will still be accessible to screen readers. The behavior of max-height and max-width are similar.

  • The list-style property: when set to none may change how some screen-reader/browser combinations announce the number of items in a list and which current item a visitor is reading.

  • The position property: should be used with care, as it can affect reading order, which must follow the DOM ordering.

  • The opacity property: when set to 0 can be used to hide content from sighted users. Does not remove content from the accessibility tree. The element is still accessible to screen readers and keyboard focus.

  • The overflow property: when set to hidden can be used to hide content from sighted users. Does not remove content from the accessibility tree. The element is still accessible to screen readers and keyboard focus.

  • The text-transform property: when set to uppercase may cause some assistive technology to read shorter words one letter at a type, as if it were an acronym.

To hide something, but not for accesibility, you can use these classes. Use a width and height of 1px for best result.

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.sr-only-focusable:active,
.sr-only-focusable:focus {
  position: static;
  width: auto;
  height: auto;
  overflow: visible;
  clip: auto;
  white-space: normal;
}

Example how to use it in your html

<nav>
  <ul
    <li class="active">Home <span class="sr-only">is active item</span></li>
    <li>Products</li>
    <li>Contact</li>
  </ul>
</nav>
Related