Menu Search
Jump to the content X X
Smashing Conf Barcelona 2016

We use ad-blockers as well, you know. We gotta keep those servers running though. Did you know that we publish useful books and run friendly conferences — crafted for pros like yourself? E.g. upcoming SmashingConf Barcelona, dedicated to smart front-end techniques and design patterns.

Notes On Client-Rendered Accessibility

As creators of the web, we bring innovative, well-designed interfaces to life. We find satisfaction in improving our craft with each design or line of code. But this push to elevate our skills can be self-serving: Does a new CSS framework or JavaScript abstraction pattern serve our users or us as developers?

Further Reading on SmashingMag Link

If a framework encourages best practices in development while also improving our workflow, it might serve both our users’ needs and ours as developers. If it encourages best practices in accessibility alongside other areas, like performance, then it has potential to improve the state of the web.

Despite our pursuit to do a better job every day, sometimes we forget about accessibility, the practice of designing and developing in a way that’s inclusive of people with disabilities. We have the power to improve lives through technology — we should use our passion for the craft to build a more accessible web.

These days, we build a lot of client-rendered web applications, also known as single-page apps, JavaScript MVCs and MV-whatever. AngularJS, React, Ember, Backbone.js, Spine: You may have used or seen one of these JavaScript frameworks in a recent project. Common user experience-related characteristics include asynchronous postbacks, animated page transitions, and dynamic UI filtering. With frameworks like these, creating a poor user experience for people with disabilities is, sadly, pretty easy. Fortunately, we can employ best practices to make things better.

In this article, we will explore techniques for building accessible client-rendered web applications, making our jobs as web creators even more worthwhile.

Clueless character making 'Whatever' gesture5
MV-whatever. (Show animated Gif6)

Semantics Link

Front-end JavaScript frameworks make it easy for us to create and consume custom HTML tags like <pizza-button>, which you’ll see in an example later on. React, AngularJS and Ember enable us to attach behavior to made-up tags with no default semantics, using JavaScript and CSS. We can even use Web Components7 now, a set of new standards holding both the promise of extensibility and a challenge to us as developers. With this much flexibility, it’s critical for users of assistive technologies such as screen readers that we use semantics to communicate what’s happening without relying on a visual experience.

Consider a common form control8: A checkbox opting you out of marketing email is pretty significant to the user experience. If it isn’t announced as “Subscribe checked check box” in a screen reader, you might have no idea you’d need to uncheck it to opt out of the subscription. In client-side web apps, it’s possible to construct a form model from user input and post JSON to a server regardless of how we mark it up — possibly even without a <form> tag. With this freedom, knowing how to create accessible forms is important.

To keep our friends with screen readers from opting in to unwanted email, we should:

  • use native inputs to easily announce their role (purpose) and state (checked or unchecked);
  • provide an accessible name using a <label>, with id and for attribute pairing — aria-label on the input or aria-labelledby pointing to another element’s id.
  <label for="subscribe">
  <input type="checkbox" id="subscribe" checked>

Native Checkbox With Label Link

If native inputs can’t be used (with good reason), create custom checkboxes with role=checkbox, aria-checked, aria-disabled and aria-required, and wire up keyboard events. See the W3C’s “Using WAI-ARIA in HTML429.”

Custom Checkbox With ARIA Link

  <some-checkbox role="checkbox" tabindex="0" aria-labelledby="subscribe" aria-checked="true">
  <some-label id="subscribe">Subscribe</some-label>

Form inputs are just one example of the use of semantic HTML10 and ARIA attributes to communicate the purpose of something — other important considerations include headings and page structure, buttons, anchors, lists and more. ARIA11, or Accessible Rich Internet Applications, exists to fill in gaps where accessibility support for HTML falls short (in theory, it can also be used for XML or SVG). As you can see from the checkbox example, ARIA requirements quickly pile up when you start writing custom elements. Native inputs, buttons and other semantic elements provide keyboard and accessibility support for free. The moment you create a custom element and bolt ARIA attributes onto it, you become responsible for managing the role and state of that element.

Although ARIA is great and capable of many things, understanding and using it is a lot of work. It also doesn’t have the broadest support. Take Dragon NaturallySpeaking12 — this assistive technology, which people use all the time to make their life easier, is just starting to gain ARIA support. Were I a browser implementer, I’d focus on native element support first, too — so it makes sense that ARIA might be added later. For this reason, use native elements, and you won’t often need to use ARIA roles or states (aria-checked, aria-disabled, aria-required, etc.). If you must create custom controls, read up on ARIA to learn the expected keyboard behavior13 and how to use attributes correctly.

Tip: Use Chrome’s Accessibility Developer Tools4114 to audit your code for errors, and you’ll get the bonus “Accessibility Properties” inspector.

AngularJS material in Chrome with accessibility inspector open15
AngularJS material in Chrome with accessibility inspector open. (View large version16)

Web Components and Accessibility Link

An important topic in a discussion on accessibility and semantics is Web Components, a set of new standards landing in browsers that enable us to natively create reusable HTML widgets. Because Web Components are still so new, the syntax is majorly in flux. In December 2014, Mozilla said it wouldn’t support HTML imports17, a seemingly obvious way to distribute new components; so, for now that technology is natively available in Chrome and Opera18 only. Additionally, up for debate is the syntax for extending native elements (see the discussion about is="" syntax19), along with how rigid the shadow DOM boundary should be. Despite these changes, here are some tips for writing semantic Web Components:

  • Small components are more reusable and easier to manage for any necessary semantics.
  • Use native elements within Web Components to gain behavior for free.
  • Element IDs within the shadow DOM do not have the same scope as the host document.
  • The same non-Web Component accessibility guidelines apply.

For more information on Web Components and accessibility, have a look at these articles:

Interactivity Link

Native elements such as buttons and inputs come prepackaged with events and properties that work easily with keyboards and assistive technologies. Leveraging these features means less work for us. However, given how easy JavaScript frameworks and CSS make it to create custom elements, such as <pizza-button>, we might have to do more work to deliver pizza from the keyboard if we choose to mark it up as a new element. For keyboard support, custom HTML tags need:

  • tabindex, preferably 0 so that you don’t have to manage the entire page’s tab order (WebAIM discusses this23);
  • a keyboard event such as keypress or keydown to trigger callback functions.

Focus Management Link

Closely related to interactivity but serving a slightly different purpose is focus management. The term “client-rendered” refers partly to a single-page browsing experience where routing is handled with JavaScript and there is no server-side page refresh. Portions of views could update the URL and replace part or all of the DOM, including where the user’s keyboard is currently focused. When this happens, focus is easily lost, creating a pretty unusable experience for people who rely on a keyboard or screen reader.

Imagine sorting a list with your keyboard’s arrow keys. If the sorting action rebuilds the DOM, then the element that you’re using will be rerendered, losing focus in the process. Unless focus is deliberately sent back to the element that was in use, you’d lose your place and have to tab all the way down to the list from the top of the page again. You might just leave the website at that point. Was it an app you needed to use for work or to find an apartment? That could be a problem.

In client-rendered frameworks, we are responsible for ensuring that focus is not lost when rerendering the DOM. The easy way to test this is to use your keyboard. If you’re focused on an item and it gets rerendered, do you bang your keyboard against the desk and start over at the top of the page or gracefully continue on your way? Here is one focus-management technique from Distiller24 using Spine, where focus is sent back into relevant content after rendering:

class App.FocusManager
$(‘body’).on ‘focusin’, (e) =>
@oldFocus =
App.bind 'rendered', (e) =>
return unless @oldFocus

if @oldFocus.getAttribute('data-focus-id')

_focusById: ->
focusId = @oldFocus.getAttribute('data-focus-id')
newFocus = document.querySelector("##{focusId}")
App.focus(newFocus) if newFocus

_focusByNodeEquality: ->
allNodes = $('body *:visible').get()
for node in allNodes
if App.equalNodes(node, @oldFocus)

In this helper class, JavaScript (implemented in CoffeeScript) binds a focusin listener to document.body that checks anytime an element is focused, using event delegation25, and it stores a reference to that focused element. The helper class also subscribes to a Spine rendered event, tapping into client-side rendering so that it can gracefully handle focus. If an element was focused before the rendering happened, it can focus an element in one of two ways. If the old node is identical to a new one somewhere in the DOM, then focus is automatically sent to it. If the node isn’t identical but has a data-focus-id attribute on it, then it looks up that id’s value and sends focus to it instead. This second method is useful for when elements aren’t identical anymore because their text has changed (for example, “item 1 of 5” becoming labeled off screen as “item 2 of 5”).

Each JavaScript MV-whatever framework will require a slightly different approach to focus management. Unfortunately, most of them won’t handle focus for you, because it’s hard for a framework to know what should be focused upon rerendering. By testing rendering transitions with your keyboard and making sure focus is not dropped, you’ll be empowered to add support to your application. If this sounds daunting, inquire in your framework’s support community about how focus management is typically handled (see React’s GitHub repo26 for an example). There are people who can help!

Cat 'helping' by laying on keyboard27
Cat “helping”. (View animated Gif28)

Notifying The User Link

There is a debate about whether client-side frameworks are actually good for users29, and plenty of people have an opinion30 on them. Clearly, most client-rendered app frameworks could improve the user experience by providing easy asynchronous UI filtering, form validation and live content updates. To make these dynamic updates more inclusive, developers should also update users of assistive technologies when something is happening away from their keyboard focus.

Imagine a scenario: You’re typing in an autocomplete widget and a list pops up, filtering options as you type. Pressing the down arrow key cycles through the available options, one by one. One technique to announce these selections would be to append messages to an ARIA live region31, a mechanism that screen readers can use to subscribe to changes in the DOM. As long as the live region exists when the element is rendered, any text appended to it with JavaScript will be announced (meaning you can’t add bind aria-live and add the first message at the same time). This is essentially how Angular Material32’s autocomplete handles dynamic screen-reader updates:

<md-autocomplete md-selected-item="ctrl.selectedItem" aria-disabled="false">
<md-autocomplete-wrap role="listbox">
  <input type="text" aria-label="{{ariaLabel}}" aria-owns="ul_001">
<ul role="presentation" id="ul_001">
  <li ng-repeat="(index, item) in $mdAutocompleteCtrl.matches" role="option" tabIndex="0">
<aria-status class="visually-hidden" role="alert">
  <p ng-repeat="message in messages">{{message}}</p>

In the simplified code above (the full directive33 and related controller34 source are on GitHub), when a user types in the md-autocomplete text input, list items for results are added to a neighboring unordered list. Another neighboring element, aria-status, gets its aria-live functionality from the alert role. When results appear, a message is appended to aria-status announcing the number of items, “There is one match” or “There are four matches,” depending on the number of options. When a user arrows through the list, that item’s text is also appended to aria-status, announcing the currently highlighted item without the user having to move focus from the input. By curating the list of messages sent to an ARIA live region, we can implement an inclusive design that goes far beyond the visual. Similar regions can be used to validate forms.

For more information on accessible client-side validation, read Marco Zehe’s “Easy ARIA Tip #3: aria-invalid and Role alert35” or Deque’s post on accessible forms36.

Conclusion Link

So far, we’ve talked about accessibility with screen readers and keyboards. Also consider readability: This includes color contrast, readable fonts and obvious interactions. In client-rendered applications, all of the typical web accessibility principles37 apply, in addition to the specific ones outlined above. The resources listed below will help you incorporate accessibility in your current or next project.

It is up to us as developers and designers to ensure that everyone can use our web applications. By knowing what makes an accessible user experience, we can serve a lot more people, and possibly even make their lives better. We need to remember that client-rendered frameworks aren’t always the right tool for the job. There are plenty of legitimate use cases for them, hence their popularity. There are definitely drawbacks to rendering everything on the client38. However, even as solutions for seamless server- and client-side rendering improve over time, these same accessibility principles of focus management, semantics and alerting the user will remain true, and they will enable more people to use your apps. Isn’t it cool that we can use our craft to help people through technology?


Thanks to Heydon Pickering for reviewing this article.

(hp, al, ml)

Footnotes Link

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
SmashingConf Barcelona 2016

Hold on, Tiger! Thank you for reading the article. Did you know that we also publish printed books and run friendly conferences – crafted for pros like you? Like SmashingConf Barcelona, on October 25–26, with smart design patterns and front-end techniques.

↑ Back to top Tweet itShare on Facebook


Marcy Sutton is an international public speaker, Angular core team member and accessibility engineer at Adobe. She is a primary contributor to ngAria, Angular's accessibility module, as well as the author of an accessibility plug-in for Protractor, the end-to-end testing framework. Recently Marcy launched Accessibility Wins, a Tumblr highlighting successes in web accessibility. She loves riding bicycles and throwing the frisbee for her dog, Wally.

  1. 1

    That’s great but have you thought about people who don’t have javascript enabled? is a great experience with disabled JS… not.

    Maybe we should build the web the way it is meant: html -> css -> js, not the other way around.

    • 2

      Marcy Sutton

      May 6, 2015 5:08 pm

      Yes, I have thought about that, and it is a concern for some users–hence my note about drawbacks to rendering on the client in the final paragraph. But people are still going to build client-rendered sites, so they might as well be informed about how to make those sites accessible.

    • 3

      Kevin Lozandier

      May 7, 2015 2:23 pm

      Some problems or a necessary amount of steps/interactive processes become discovered critical to a user’s experience that’ll require JavaScript towards a reasonable base experience.

      In such a scenario, one that should overcome heavy scrutinity, extensive JavaScript may be required and frameworks that leverage client-side rendering can perhaps be justified to exploit organization & best practices of Implementimg a solution that follows this path.

      This is often the case of web apps that must conveniently present content or interactive experiences the Web hasn’t accounted for comprehensively yet like nuanced situations involving video (YouTube, NetFlix), code challenge panels (Code School, Khahn Academy, Treehouse, & etc) or particular internationalization concerns.

  2. 4

    Kevin Lozandier

    May 7, 2015 4:29 am

    Unsurprisingly, this was a fantastic read; I strangely never heard about `aria-owns`, will definitely review instances I should have used it when it comes to directives & web components!

    Focus management one of the hardest things to get right, thanks for finding sharing Distiller’s way of handling focus that felt right for them!

    Thanks for your work pairing with Julie to create the Protractor Accessibility Plugin!1

    1:For those who don’t use Angular, Protractor is just as awesome for non-Angular projects. Protractor being very “hook-friendly”, it’s just as awesome on non-angular sites being a treat to hook up to Sauce Labs & Appium to analyze how well your web applications runs on a variety of desktop & mobile browsers in addition to your device lab testing.

  3. 5

    Chris Hore

    May 7, 2015 5:26 am

    This article isn’t just a primer for client-rendered apps but should be compulsory reading for all devs who live in the real world where accessibility is so often an afterthought or being retrofitted into legacy systems.

  4. 6

    James Edwards

    May 7, 2015 2:21 pm

    FYI: role=”alert” doesn’t work in IE+Jaws (it won’t be announced at all).

    It will be announced if you add aria-live=”assertive” in addition to the role, but it won’t be assertive, it will be treated as polite.

    • 7

      Kevin Lozandier

      May 7, 2015 2:28 pm

      For legacy concerns, I’ve read once to use both role=”alert” & aria-live=”assertive” for immediate information & use role=”status” & aria-live=”polite” for post-action information

      • 8

        Heydon Pickering

        July 24, 2015 8:50 am

        I think you read that from me, in Apps For All :-) It’s called “maximizing compatibility” :-)

    • 9

      Marcy Sutton

      May 7, 2015 6:57 pm

      Thanks for the tip! Any specific version of JAWS/IE?

  5. 10


    May 17, 2015 10:33 am

    thanks to you for your nice tips and hope so these will be very effective for me. i will try to apply your tips in my world field must


↑ Back to top