Paddy UK
Paddy UK

Reputation: 45

How can I get NVDA and similar to read out aria-label AND content of <foreignobject>?

I have an SVG tree graphic which I'd like to make accessible to users of NVDA and similar.

Each node in the SVG graphic is coded like this:

<g class="node" >
<foreignObject tabindex="0" focusable="true" aria-label="I want this read by screen readers such as NVDA..." class="nodestyle">...and I'd like this read out as well.</foreignObject>
</g>

At the moment when I tab to each node, Chrome just reads out the text in the foreignobject, and Edge Chromium only reads out the aria-label text in the g. I've tried using title, label, etc in both the g and foreignobject, but I can't get either browser to read out both pieces of text. Can anyone suggest how I can achieve this, if it's possible at all?

I don't want to do hacky things like putting in extra text and then hiding it with CSS.

Upvotes: 3

Views: 827

Answers (2)

brennanyoung
brennanyoung

Reputation: 6514

I have been doing something quite similar, although I don't have any open/close hide/show behavior.

This is very much not a solved problem, so I am curious about any work done in this space, and I hope you're up for further correspondence! Do check this article. I think it's part of any proper solution. This one is worth a look, too.

I've tried many things, also settling on SVG with foreign object, and tabbing to navigate. Even implemented a couple of different mechanisms to choose branches.

The tricky thing is choosing branches. There's an attribute called aria-flowto which takes one or more 'destination' id as a value, but it has very poor support, and there are no recommended idioms for selecting amongst them. (It can be made to work with javascript, however).

One of my attempts uses javaScript to copy the contents of the boxes in to an aria-label attached to the element with tabindex=0 on it. This works pretty well, but does not give any of the affordances of 'browsable' content. Again, announcing branches is a challenge.

If you want the markup inside the foreignObject to be browsable using semantic affordances such as headings, you'll probably have to use role=document, which many a11y community members will no doubt warn you against because it's tricky. I've been able to get role=document working fine in HTML, but haven't yet integrated it with SVG. I don't yet know if it will play well with the WAI-ARIA Graphics Module roles.

Upvotes: 1

GrahamTheDev
GrahamTheDev

Reputation: 24825

Sadly with the example you have this is going to be very difficult to implement (not the labelling part but accessibility in general due to the document structure).

First thing you need to do is fix the DOM order of the SVGs - the first node you land on is 'no' which is confusing and fixing the DOM order is a lot easier than trying to manage tabindex.

The next thing is that you need to show that activating an item (i.e. 'yes') has an effect of loading more options.

For this I would follow conventions of collapsible content - this accordion example should help you understand how to use aria-expanded and aria-controls correctly. (but only use it for understanding those concepts, it isn't the right solution for your problem.)

Next you need to consider letting a user know what options have now been presented to them. The simplest way to do this is to wrap the SVGs in an <ul> as then a screen reader will let them know how many items there are to choose from (within the expanded section).

With that in mind you should look into how a treeview is structured as that will give you the closest example of how to structure complex nested structures.

Also look at this file explorer tree view example which is similar to see that with correct nesting you do not need to worry about labelling too much. (as you can just use the <title> property of your SVGs as the content if you find the plain text is not read out correctly).

What you will notice in the above examples is that the 'parent node' text is actually contained within the <li role="treeitem"> so that when you select a sub-item it reads both the parent and the selected item out together automatically.

Trying to do all of the above with just the SVGs will cause you numerous headaches with workarounds (i.e. you could use aria-labelledby with multiple IDs on child items to achieve what you originally asked but then you have lots of maintainability issues) so I would recommend looking at trying to copy a treeview structure and keep your SVGs simple.

The final advantage of the treeview method is that users will be familiar with how to control the treeview (arrow keys and space to expand / collapse) so you don't need to try and educate them as to what controls to use.

Example

A bit hard to read but hopefully should give you an idea of what your end structure should look like. I have removed a lot of the positioning attributes etc. to try and make it easier to read.

<h3 id="tree_lbl">
  Decision Tree
</h3>
<ul role="tree" aria-labelledby="tree_lbl">
  <li role="treeitem" aria-expanded="false">
      <g class="node">
        <foreignObject tabindex="0" focusable="true" class="nodestyle">Is this a decision tree?</foreignObject>
      </g>
    <ul role="group">
      <li role="treeitem" aria-expanded="false">
        <g class="node">
          <foreignObject tabindex="0" focusable="true" class="nodestyle">Yes</foreignObject>
        </g>
        <ul role="group">
          <li role="treeitem">
            <g class="node"><foreignObject tabindex="0" focusable="true" class="nodestyle">A</foreignObject></g>
          </li>
          <li role="treeitem">
            <g class="node">
              <foreignObject tabindex="0" focusable="true" class="nodestyle">B</foreignObject>
            </g>
          </li>
        </ul>
      </li>
      <li role="treeitem" aria-expanded="false">
        <span>
          <g class="node">
              <foreignObject tabindex="0" focusable="true" class="nodestyle">No</foreignObject></g>
        </span>
        <ul role="group">
          <li role="treeitem" class="doc">
            <g class="node">
              <foreignObject tabindex="0" focusable="true" class="nodestyle">A1</foreignObject>
            </g>
          </li>
          <li role="treeitem" class="doc">
            <g class="node">
              <foreignObject tabindex="0" focusable="true" class="nodestyle">B1</foreignObject>
            </g>
          </li>
        </ul>
      </li>
    </ul>
  </li>
  </ul>

Upvotes: 1

Related Questions