A reminder of how to add a localized accessible name to a button – without incurring any new localization costs
I was looking at a pretty important app the other day, and considering how many of the buttons in its UI didn’t have accessible names. This problem is interesting in that it renders the app useless to many customers, and yet it can often be quick ‘n’ easy to fix.
Knowing there’s a problem
Before an accessibility bug can be fixed, we need to know there’s a problem. So we could point end-user tools like screen readers at UI, and see how those tools behave. But for me, I always point the Inspect SDK tool at the UI first.
The XAML buttons I was looking at in the important app seemed like they contained multiple elements. For example, say I write the following XAML…
<Button Click="Button_Click" Margin="20">
<StackPanel Orientation="Horizontal">
<Image x:Uid="ButtonImage"
Source="ms-appx:///Assets/Jasmine.png"
Width="100" Height="100" />
<StackPanel>
<TextBlock x:Uid="ButtonTitle"
FontWeight="Bold" />
<TextBlock x:Uid="ButtonDescription" />
</StackPanel>
</StackPanel>
</Button>
And add the following localizable strings, for the text on the two TextBlocks, and for the accessible name of the Image.
ButtonTitle.Text Walk Jasmine
ButtonDescription.Text Watch out for coyotes
ButtonImage.AutomationProperties.Name Jasmine the spaniel
When I run the app, I get a button containing an Image, and two TextBlocks as expected. If I then point Inspect to the button, in order for me to learn how my UI is being represented through the UI Automation (UIA) tree of elements, I can see the accessible names for the three contained elements, as shown in the screenshot below.
Figure 1: Inspect reporting the accessible names of the three elements contained in the button.
However, the critically important thing here is that the button itself has no accessible name. So my customers who use a screen reader will reach the button, and only get told they’re at a button. They’ll not be told what the purpose of the button is. Technically, they could probably move the screen reader into the contents of the button to find out more information about it, but that’s a really tedious user experience. I don’t make my sighted customers interact with the button before I reveal its purpose visually. So why would I treat my customers who use screen readers any differently?
By the way, the reason why there are elements called "ButtonNames" in the UIA tree for my app, is because that's the name of my test app, and by default the app name appears as the accessible name for a couple of high-level elements in the UIA tree.
Fixing the problem
So now I know there’s a problem with my button, I’ll set about fixing it.
One way for me to add an accessible name for the button would be for me to add a new localizable string, and connect that up to the button. After all, I just did that for the image contained inside the button. So by adding the x:Uid to the Button, and a new localizable string resource, I could fix the problem.
While that would work, (and really is pretty quick to do,) I have now incurred the cost of having to have that new string localized in all the languages that my app ships. Each string taken in isolation doesn’t seem like a big deal for localization, but costs can build up after a while, and so I’d really not want to add a new string that needs localizing unless I have to.
And the interesting thing here is that my app already has a localized string that I could consider leveraging. After all, the button shows “Walk Jasmine” visually, so perhaps I’ll use that for the accessible name of the button.
This is where I should consider what the most helpful and concise accessible name for the button would be. The “Walk Jasmine” text actually seems perfect, as it concisely conveys exactly what the purpose of the button is. To append the additional “Watch out for coyotes” string to the accessible name would seem like too much information. (Maybe I’ll set that additional information as the HelpText property on the button. I’ll get back to that later…)
Ok, now that I’ve decided I can leverage the existing localized string for the accessible name of the button, how do I do that? Well, I add XAML markup to say that the button’s accessible name is to be set from the accessible name of the TextBlock which shows the text visually. (By default, a TextBlock’s accessible name is set from the text that’s shown visually on the button.)
So, I added the XAML highlighted below…
<Button Click="Button_Click" Margin="20"
AutomationProperties.LabeledBy="{Binding ElementName=ButtonTitle}">
<StackPanel Orientation="Horizontal">
<Image x:Uid="ButtonImage"
Source="ms-appx:///Assets/Jasmine.png"
Width="100" Height="100" />
<StackPanel>
<TextBlock x:Uid="ButtonTitle"
Name="ButtonTitle"
FontWeight="Bold" />
<TextBlock x:Uid="ButtonDescription" />
</StackPanel>
</StackPanel>
</Button>
Now when I run the app and point Inspect to it, I can see that the button now has the helpful, concise and localized accessible name, without me having increased the localization costs of my app. Smashing.
Figure 2:
So is that it?
With that small change above, I’ve prevented my customers from being irritated when they reach my button. Instead of them knowing that there’s something important available to them, but not being told what it is, they’ll know immediately that they’ve reached the “Walk Jasmine” button.
But to ship an app that meets the lofty goal of not irritating my customers, doesn’t sound like much of an achievement to me. I’d also like to ship an app that’s as efficient to use as I can make it. And that means I don’t want to expose UI elements to my customers unless those elements are actually useful to them.
The button I’ve built is exposed as four elements in the UIA tree of elements, and all of these elements are easy for my customers to reach. I should now consider how useful it is for my customer to reach all the elements. It really wouldn’t seem useful for my customer to reach the title TextBlock, because that same text is now being exposed as the name of the button. So I’ll set the following XAML markup on the title TextBlock, to move it out of the Control view of the UIA tree. (The Control view should contain everything of interest to my customers, and only the things of interest. More details on this can be found at I’ll say this only once - Your customers need an efficient experience.)
<TextBlock x:Uid="ButtonTitle"
AutomationProperties.AccessibilityView=”Raw”
Name="ButtonTitle"
FontWeight="Bold" />
When I now use Inspect to show me the Control view of the UIA tree, I no longer see the title TextBlock contained within the button. XAML continues to let me set the accessible name of the button in the Control view of the UIA tree, from a TextBlock that I’ve just moved out of the Control view. That’s rather handy.
Figure 3: Inspect showing that the title TextBlock is no longer being exposed through the Control view of the UIA tree.
So I next consider whether I should do anything with the “Watch out for coyotes” TextBlock. That TextBlock presents information that is useful to my customers, and which isn’t presented anywhere else. So that information must be exposed through the Control view of the UIA tree. So I’d probably just leave it as it is.
I did wonder if I might remove that TextBlock from the Control view of the UIA tree, and set the HelpText of the button to be the same text as that shown visually on the TextBlock. (Depending on what screen reader settings my customer’s chosen, my customer may hear the HelpText string when they reach the button.) But to do this would require me to explicitly add a new localized string to my app, for the AutomationProperties.HelpText of the button. I don’t want to incur any new localization costs for this, so I’m not going to change anything to do with this TextBlock.
That just leaves the image in the button. For images that are purely decorative, I remove them from the Control view of the UIA tree, as they’re not particularly helpful to my customers. I've decided to leave my image in the Control view in this test app, but really, if this was a shipping app, I’d probably have more in-depth discussions about the image. Perhaps this is the only place in the app where my sighted customers can learn something of what Jasmine the spaniel looks like. And if that's the case, I’d leave the image in the Control view, and update its accessible name and HelpText such that more information is being conveyed programmatically.
The upshot of all this is that my button now has a helpful, concise and localized accessible name, with no additional localization costs for me. And only the useful information inside the button is being exposed through the Control view of the UIA tree. The screenshot below shows the Narrator screen reader interacting with the button, and I hear it announce "Walk Jasmine, button". That's exactly what my customers should hear.
Figure 4: Narrator highlighting the button, with a desktop image of Jasmine the spaniel shown behind the app.
Conclusion
I’d say that the problem of buttons having no accessible names is still one of the most important accessibility issues that apps ship with today. In some cases, it probably seems fair enough that you need to add an accessible name yourself. For example, if my button only contained the image, (with no text,) there's no way the XAML UI framework could somehow figure out that the accessible name should be “Jasmine”.
In other cases you may feel that perhaps XAML could take some of the text contained inside the button, and repurpose it as the accessible name. Today that’s just not how the platform works. So as part of making all your great functionality available to all your customers, regardless of how they interact with their device, you might need to use the LabeledBy markup. It’s pretty quick to do once you know about it. These are the bits of interest...
<Button AutomationProperties.LabeledBy="{Binding ElementName=ButtonTitle}" ... >
<TextBlock Name="ButtonTitle" ... />
</Button>
And y’know, given that this post relates to how people are sometimes made aware that something’s available to them, but they’re not told what it is, I was tempted to upload this post with no title. But maybe that wouldn’t have been such a good idea…
Guy