One way to prevent avoidable issues is to approach building for accessibility the way you build a web or native app from the ground up, in a stack of technologies, where each one has its role to play. Just like you carefully consider what backend language (if any), framework language, and additional libraries you might want to use for your app before you start building, the same should be done for accessibility.

In the coming months, we’ll be discussing and deconstructing what you know about accessibility by rethinking our most basic assumptions about it, from where it should begin to who it includes, and where it fits in a larger inclusivity strategy for your organization. We’ll share case studies, tutorials, and things we’ve learned through usability testing with real users to help you refresh your thinking about accessibility and what it really means for designing, building, and testing your products and services. Stay tuned!

A development stack for accessibility

When first starting out, it can be tempting to “add” accessibility after front-end development work is done. However, just like any development process that tacks additional functionality onto a feature that’s perceived as being done, this approach can quickly result in accessibility solutions that are over-engineered, fragile, or too focused on a single user group to the exclusion of others.

It’s frustrating and stressful to try to make a feature accessible after the fact. That shopping cart preview that’s viewed on mouse hover looks great, but it needs to be keyboard accessible, too. The seatpicker widget is arrayed in a responsive grid, but the grid isn’t a table, and the individual buttons don’t have labels available to assistive technology users or speech-to-text users. And that slick drag-and-drop interaction to organize your custom Kanban board, which is all built with <div> elements because that’s what the framework used? That’s going to be a pain to rebuild.

One way to prevent these types of issues is to approach building for accessibility the way you build a web or native app from the ground up, in a stack of technologies, where each one has its role to play. Just like you carefully consider what backend language (if any), framework language, and additional libraries you might want to use for your app before you start building, the same should be done for accessibility.

Seeing the big picture

Before starting to code, it’s important to put some thought into choosing the accessibility stack level that’s right for the project. Front end web developers who have embraced JavaScript frameworks for full-stack projects will be familiar with approaching development from an architectural perspective as well as a behavioral one. It’s worthwhile to take a similar approach in integrating accessibility from the beginning as you build features, to take into account the scope, goals, and user needs related to the specific feature.

Just like you decide what back-to-front tech stack you want to use, you’ll need to decide what features of HTML, CSS, JavaScript, and ARIA make sense in combination to solve your problem and provide the best user experience. Ultimately, baking accessibility in from the start saves time and pain later, and results in a healthier code base–having clear goals for your users and scoping code features into a stack can help.

Solving for inclusivity

There are a few main questions to ask before you begin deciding what level of the stack to use:

What content, context, and functionality does the user need to use and understand the feature? How is this information conveyed to all users in code? What is the lowest-stack solution for helping users reach that goal?

The first two steps should seem familiar, since they represent the basis for good UX and development design practice. But that last question might seem at odds with the previous ones, so let’s break it down.

The accessibility stack

To start thinking about how to scale your web app, it can be helpful to group development problems into four levels of tools, where each level is used to solve a problem that the level before it can’t on its own. If you’ve done accessibility work in the past, you can probably guess what at least most of the stack is:

HTML gives the structure and order of content. CSS provides presentation. JavaScript lends behaviour. ARIA creates additional “rich interactions” for assistive technology users.

While these four seemingly simple parts conjure a nicely balanced layer cake, the truth is that some problems will be solved with only one part of the stack, maybe two, maybe three. Really, the shape is less of a cake and more of a mountain or pyramid, where the lower levels provide the most robust support to the most users. Every feature (and user) needs HTML, and from there, each layer serves an important, but less substantial, purpose, until the top of the mountain finishes off the feature stack with support for the smallest user group.

HTML only

No moving parts or special styles needed? HTML alone will do the trick! Using native HTML and best practices for content order, interactive elements, navigation, and labeling of controls ensures that users will encounter the default behaviors they expect. The majority of accessibility issues we see aren’t found in JavaScript or ARIA, but in good ol’ HTML. So, starting here saves a great deal of time and effort.

When done right, HTML should:

Address how content is weighed and conveyed to all users via semantic structure. Not only assistive technology users benefit; users who rely on custom styles and layouts need the baseline structure for their preferences to work. While it’s possible to add “static” content via CSS and JavaScript, using HTML ensures that your content will be available to users regardless of their device, software, and any customizations they’ve made to fit their needs and preferences.

Provide the order that focusable content receives tab focus for interactive elements on the page (for users who rely on the keyboard or a similar input device), as well as the order it’s accessed by with assistive technology users.

Convey the default text alternative or decorative nature of an image via the alt attribute for assistive technology users and those who turn off images.

Create native interactive features like links, buttons, and form fields, along with programmatically available text and labels, that provide support for all users. Native features that always look and behave normally give clear context for assistive technology users, users with cognitive disabilities, and users targeting elements on the screen with speech-to-text software.

HTML and CSS

Use CSS to enhance or complement the content provided by HTML, or hide content that users aren’t meant to have access to yet. Need to provide extra text for assistive technology users only? Do it with CSS. (Or consider allowing it to be visible in native HTML for all users!)

When implemented with accessibility in mind, CSS:

Hides or shows content for all users, or shows content to assistive technology only, which can be hidden in HTML with CSS.

Adjusts the layout based on viewport or device size. Keep in mind that users with low vision may trigger layouts designed for smaller screens on larger ones by increasing the font size or using OS-native or third party magnification tools. Remember, too, that floating content right can cause a mismatch between the visual, “correct” order of content and the order that assistive technology users find it.

Provides high contrast-friendly styles to users with low vision who use OS-level or custom color schemes, as well as for users who have issues with color contrast. (Remember to give anything with a background color or image a border, and provide a text alternative for information conveyed with color alone.)

HTML, CSS, and JavaScript

Inclusive JavaScript starts with making standards-based HTML and CSS come alive, not by replacing default functionality. JavaScript-driven experiences can be fully accessible when built on top of a solid HTML and CSS foundation, and when full care is taken to provide full keyboard support.

When used carefully and appropriately, JavaScript:

Allows users, especially those with vision, motor, and cognitive issues, to make dynamic changes easily and intuitively by managing keyboard focus.

Inserts and removes content in logical places in the DOM to help support logical reading order, focus order, and contextual understanding.

Changes values for attributes and adds and removes attributes to support accessible content and CSS updates.

Provides for custom keystrokes or other inputs for complex, non-native features. (Don’t forget to let users know how to use your custom features, if you build them, using HTML and CSS.)

HTML, CSS, JavaScript, and ARIA

Congratulations, you’ve built a full custom feature! Now it’s time to pull out all the stops. You start with native semantic HTML and valid CSS, use JavaScript to handle behavior, map keystrokes for custom controls, and update states and values. Finally, you’re ready to use ARIA to convey the visual cues, non-native controls, and updates made to assistive technology users that can’t be provided otherwise.

A ship-shape ARIA implementation includes these considerations:

ARIA is only used when HTML, CSS, and JavaScript can’t provide the solution. Since ARIA only provides support to a cross section of users (those that rely on an assistive technology), it’s important to make sure you’re not relying on it alone to support with low vision, cognitive issues, and motor/mobility issues.

Since ARIA only provides support to a cross section of users (those that rely on an assistive technology), it’s important to make sure you’re not relying on it alone to support with low vision, cognitive issues, and motor/mobility issues. ARIA is not a cure-all, even for assistive technologies. Just like browsers handle HTML5 elements differently, assistive technologies respond differently to ARIA functionality. Using ARIA sparingly and doing your research to see which features are best supported will help provide progressive support where it’s needed.

It’s implemented for elements that it’s specified for, and uses valid attribute values. Unlike data attributes, ARIA attributes have names and values specified by W3C guidelines to work correctly between user agents (that is, browsers and assistive technologies).

Other combinations

It’s, of course, possible and sometimes necessary to combine the stacks in a different order (say, HTML and ARIA only), when you’re building your own features from scratch. For example, using aria-hidden is recommended for hiding icon fonts from assistive technologies, since different types of software parse content introduced in CSS differently.

In general, though, using ARIA to override native HTML assumes that users are using assistive technologies that have full ARIA support, or any ARIA support at all. And skipping the CSS or JavaScript support layers can mean exposing information to users at the wrong time, or failing to provide full keyboard support. Removing middle layers from the stack makes it uneven, and means either making assumptions about what technologies your users will be using or forgetting some of your use cases.

If you do need to skip a layer, just keep in mind that the pyramid requires a wider base than it does a peak, so make native, semantic HTML the foundation for every solution to keep your pyramid stable.

Examples:

Taking action

The first step is to get better acquainted with the semantics of the HTML5 specification. You might be surprised what it can do for you all on its own.

Next is to brush up on CSS, and not just emerging CSS4 functionality. Remind yourself about what CSS properties actually do, such as which ones hide content from users and which don’t.

Third, think about your JavaScript practice. Do you know what your framework’s doing when you call it? Do you know its shortfalls, and how to make up for them?

Next, review the ARIA specification, especially the roles, and cast a close eye over the recommendations for using ARIA in HTML. Make sure you’re not trying to do in ARIA what can be done in a lower part of the stack.

And finally, determine whether what you’ve built is actually usable. Don’t be afraid to run your work through its paces with actual users, and to learn from how they actually interact with your code. In the end, the stack is designed to make your work work for them.