This article describes the implementation of an accessible online shop and refers to the adjustments made to the frontend implementation with the goal of enabling shop visitors to navigate using assistive technologies such as screen readers and voice control. The code adjustments and illustrations in this article are related to our Intershop Progressive Web App, but the basic modifications are applicable to any other frontend as well. Please note that the Intershop Progressive Web App is not yet fully accessible by default. Due to common adjustments made to this template during project development, some parts for accessibility are not included.
The following examples are not the only way to achieve accessibility. Other solutions can also make the content accessible. For the sake of clarity, some parts of the code from the Intershop Progressive Web App are not included in the examples.
All adjustments refer to the Web Content Accessibility Guidelines 2.1 (WCAG 2.1) whereby all changes mentioned here do not aim to achieve a specific level of WCAG 2.1. The solutions shown will by no means remove all barriers for people with a disability. Nevertheless, they improve the overall accessibility of an online shop without requiring major design changes.
Further details about the WCAG 2.1 can be found here: Web Content Accessibility Guidelines (WCAG) 2.1
Watch this short introduction video to get an idea of what accessibility in an online shop is about.
This section describes basic adjustments of all web pages of an online shop.
The title of all web pages should be set through the <title>
element that represents the topic or purpose. The same applies to the lang
attribute that represents the default language of the web page.
Example | Illustration | Code |
---|---|---|
Title: "inTRONICS Home" | | Title and Language of a Web Page <html lang="en"> <head> <title> inTRONICS Home </title> </head> </html> |
Structural and semantic markup as well as color should be used according to specification.
Headings should be specified through the <h1>
to <h6>
elements.
Example | Illustration | Code |
---|---|---|
Heading in the footer | Heading: "SERVICE" <h4>SERVICE</h4> [...] |
Alternative input devices refer to the keyboard or voice control in this section. The following points should be considered in this context:
Every functionality should support keyboard controls.
No specific timings needed for individual keystrokes.
Users cannot be trapped into content.
Focusing specific content should not trigger changes of context (e.g. extend a navigation by a keydown
event instead of focus
).
A logical tab order through focusable and interactive components should be created by using the tabindex
attribute.
Since focus
should not trigger changes of content, expandable content (e.g. navigation, pop-up, drop-down) should become fully accessible by alternative solutions. This can, for example, be implemented by JavaScript methods. On the example of an expandable category navigation, it could be sufficient to provide an alternative way to access all sub-categories, e.g. by using a clickstream (e.g. click Computers > click Tablets > click Acer) or an expanded navigation on another area. Both alternatives are available in the Intershop Progressive Web App.
In general, any non-text content should be given an alternative in text form. Where visible labels cannot be used, an aria-label
attribute to provide an invisible label, or the aria-labelledby
attribute should be used instead.
Example | Illustration | Code |
---|---|---|
Parent navigational item "COMPUTERS" shows child navigational items when hovering over it | 3.2.1.1.1. Main Navigation<nav aria-label="main navigation"> [...] <ul> <li> <a tabindex="0" href="http://www.example.com"> Computers </a> [...] <ul> <li> <a tabindex="0" href="http://www.example.com"> Tablets </a> [...] </li> [...] </ul> [] </li> [...] </ul> [...] </nav>
|
Keyboard-only users are dependent on their tab key to navigate within the online shop. For this reason, a logical tab order through all focusable components should be created. Therefore, the tabindex
attribute can be used (-1 = focusable but not through the keyboard, 0 = focusable also with the keyboard). However, it is only advisable to use the tabindex
attribute with the values “-1” or “0”. The tab order should be specified through the structural and semantic markup of the webpage while using the tabindex
value “0”. Making non-interactive components focusable through the tabindex
attribute is also not advised unless it is necessary for the functionality.
Additionally, it can be important to make content hidden but still focusable (e.g. for links that are used to bypass blocks of repeated content). To implement this, the following example adjustments could be made to the contents styling:
HTML example for a skip link that refers to a style.css specific class called skip
:
Link "Skip" with reference to css class .skip of style.css file
<link rel="stylesheet" type="text/css" href="style.css"> [...] <a class=”skip” tabindex="0" href="#skip"> Skip </a>
CSS code snippet in style.css for class skip:
Example style.css code snippet for hiding links
.skip{ width: 1px; height: 1px; overflow: hidden; position: absolute; left: -10000px; top: auto; }
Reading sequence determines the interpretability of coherent text modules through assistive technologies and the ability to navigate user-friendly and quickly to sections (e.g. by skipping the header or jumping directly to the main area of a page). The following points should be considered in this context:
The correct tabbing behavior should be used (see Tabbing through Components ).
A link at the beginning of repeated blocks of content that skips them should be provided to give the customer the opportunity to bypass repeated content. If possible, regular headings should be provided so that the customer can jump to them.
Example | Illustration | Code |
---|---|---|
Skip the navigation Link |
| Visible Link: "Skip the navigation" <a tabindex="0" href="#skipNav"> Skip the navigation </a> [...] <span id="skipNav"></span> Hidden Link: "Skip the navigation" <a class=”skip” tabindex="0" href="#skipNav"> Skip the navigation </a> [...] <span id="skipNav"></span> Additional CSS code is necessary to hide the Skip the navigation link (see example code in section Tabbing through Components). |
A link (visible or hidden) at the top of the page that leads directly to the main content area should be provided as the first focusable component on a webpage.
Example | Illustration | Code |
---|---|---|
Skip the header and go directly to the main content |
| Link: "Skip to main content" in the Header <header> <a class=”skip” tabindex="0" href="#skipToMain"> Skip to main content </a> [...] </header> Main Content Area <div role="main" id="skipToMain"> [...] </div> Additional CSS code is necessary to hide the Skip the navigation link (see example code in section Tabbing through Components ). |
A sequence of text content that represents the correct order and meaning (coherent, regardless of e.g. style changes) should be determinable by assistive technologies.
Color should not be the only visual mean. Therefore, provide patterns that are independent of color or alternatives in text form.
Example | Illustration | Code |
---|---|---|
Menu for changing the product color | Menu to change the Color <label for="colorOfProducts">Color</label> <ul id="colorOfProducts"> <li> <a tabindex="0" title="Platinum"> <span style="background-color: #a2a5ac"></span> </a> </li> [...] </ul> |
It is advisable to provide subtitles and descriptions in text form for audio and video that present the same information.
A contrast ratio of at least 3:1 should be used for text content and its surroundings.
The header and footer of an online shop usually consist of focusable interactive components, while the main area additionally features relevant text content. Therefore, the most important information such as product information should be available to e.g. screen readers without requiring a prior action such as opening an expandable menu.
Negative example | Positive example |
---|---|
When an input error occurs, the erroneous item should be identified through the aria-invalid
attribute.
The error should be described in text form by using the aria-describedby
and the role="alertdialog"
attribute.
The error description should link to the erroneous item through the <a>
element.
It is also possible to use a separate error section that groups multiple error messages linking to the item in error.
Mandatory <input>
or <select>
elements can be specified by an aria-required
attribute or by a symbol (e.g. "*") that is described through the <abbr>
element.
Format hints (e.g. Date (mm-dd-yyyy)) can be added to a label to prevent input errors if necessary.
Example | Illustration | Code |
---|---|---|
Multiple error messages grouped to an error section | 3.6.1.1.1. Error Section<ish-error-message tabindex="0" aria-describedby="alertText" role="alertdialog"> <div> <h4> The following errors occured: </h4> <ul id="alertText"> <li> <a tabindex="0" href="#firstName" title="First Name"> First name is mandatory. Please provide a first name. </a> </li> [...] </ul> </div> </ish-error-message> 3.6.1.1.2. Label: "First Name" with Input Field[...] <input tabindex="0" aria-invalid="true" type="text" id="firstName"> [...] | |
Error message for an input element | 3.6.1.1.3. Label: "First Name" with Input Field[...] <input tabindex="0" aria-invalid="true" type="text" id="firstName"> [...] 3.6.1.1.4. Error Text<ish-form-control-feedback tabindex="0" aria-describedby="alertText" role="alertdialog"> <a tabindex="0" href="#firstName" title="First Name"> <small id="alertText"> First name is mandatory. Please provide a first name. </small> </a> </ish-form-control-feedback> | |
Error message for a select element | 3.6.1.1.5. Label: "Country" with Select Element[...] <select tabindex="0" aria-invalid="true" id="selectCountry"> [...] </select> [...] 3.6.1.1.6. Error Text<ish-form-control-feedback tabindex="0" aria-describedby="alertText" role="alertdialog"> <a tabindex="0" href="#selectCountry" title="Country"> <small id="alertText"> Please select a country. </small> </a> </ish-form-control-feedback> | |
Error message for a button | 3.6.1.1.7. Button: "Sign In"<button tabindex="0" aria-invalid="true" type="submit" id="signIn"> Sign In </button> 3.6.1.1.8. Error Text<div tabindex="0" aria-describedby="alertText" role="alertdialog"> <a tabindex="0" href="#signIn" title="Sign In"> <span id="alertText"> Your E-mail/Password combination is incorrect. Please try again. </span> </a> </div> |
This section describes what changes can be made to specific content of a common online shop.
Structural and semantic markup should be used.
Headings should be provided through the <h1>
to <h6>
elements.
Alternatives in text form should be provided if information is solely reliant on the text's formatting.
Example | Illustration | Code |
---|---|---|
Addresses | 4.1.1.1.1. Invoice Address<div> <h5>Invoice Address</h5> </div> [...] <address> <span aria-label="Company name"> Oil Corp </span> <br> <span aria-label="Full name"> Mark Zimmermann </span> <br> [...] </address> [...] |
An alternative in text form should be provided for links via the link text or the title
attribute.
It is advised to use the <ol>
, <ul>
and <dl>
elements for lists of links.
A navigation should be specified by the <nav>
element.
It is advised to use the link text, the links enclosing sentence, paragraph, list item, table cell or the title
/ aria-label
attribute to identify a links purpose.
New windows, tabs or pop-ups should only open from links when necessary while giving an advanced warning before doing so.
Example | Illustration | Code |
---|---|---|
An image with a link | 4.2.1.1.1. Image with a Link: "Microsoft Surface Pro"<a tabindex="0" title="Microsoft Surface Pro" href="http://www.example.com"> [...] <img alt="Microsoft Surface Pro" src="http://www.example.com" itemprop="image"> [...] </a> | |
The navigation | 4.2.1.1.2. Link: "Skip the navigation"<a class="skip" tabindex="0" href="#skipNav"> Skip the navigation </a> 4.2.1.1.3. Main Navigation<nav aria-label="main navigation"> [...] <ul> <li> <a tabindex="0" href="http://www.example.com"> Computers </a> [...] </li> <li> <a tabindex="0" href="http://www.example.com"> Conferencing </a> [...] </li> [...] </ul> [...] </nav> <span id="skipNav"></span> Additional CSS code is necessary to hide the "Skip the navigation" link (see example code in section Tabbing through Components ). | |
A text link (e.g. for opening a product page) | 4.2.1.1.4. Link: "Microsoft Surface Laptop"<a tabindex="0" href="http://www.example.com"> Microsoft Surface Laptop </a> | |
A text link that opens a new window or tab | 4.2.1.1.5. Link: "Read our Security & Privacy Policy (opens in new window)"<a target="_blank" tabindex="0" href="http://www.example.com"> Read our Security & Privacy Policy (opens in new window) </a> |
An alternative in text form should be provided for images through the alt
attribute.
If the element has a role="img"
as an attribute, the alternative should be provided through the aria-label
or the aria-labelledby
attribute.
It is advised to use the <img>
element or provide role="img"
as an attribute for images.
The functionality of the following icons has been removed in the code section to focus on their alternatives in text form.
Example | Illustration | Code |
---|---|---|
Product image | 4.3.1.1.1. Image: "Microsoft Surface Laptop Platinum product photo"<img alt="Microsoft Surface Laptop Platinum product photo" src="http://www.example.com"> | |
Icon with an | 4.3.1.1.2. Icon: "Edit"<svg aria-label="Edit" role="img" data-icon="pencil-alt"> [...] </svg> | |
Icon with an | 4.3.1.1.3. Icon: "Add to order template"<svg aria-labelledby="template-title" role="img" data-icon="list"> <title id="template-title">Add to order template</title> [...] </svg> |
It is advised to provide an alternative in text form for <input>
and <select>
elements through the <label>
element / aria-label
attribute or the title
attribute if labels cannot be used.
The <optgroup>
elements should be used to group related <option>
elements of a <select>
element.
Additionally, to prevent major automatic changes of content by changing the setting of an <input>
or <select>
element, a button can be provided through the <button>
element that needs to be pressed to initiate a change of content or a text description on how content will change can be used.
Changes according to Error Handling should be applied.
Example | Illustration | Code |
---|---|---|
Text inputs | 4.4.1.1.1. Label: "First Name" with Input Field<label for="firstName"> First Name <abbr title="mandatory field">*</abbr> </label> [...] <input tabindex="0" type="text" id="firstName"> | |
Checkboxes | 4.4.1.1.2. Label: "I agree to the Terms & Condition." with Input Field<label for="termsAndConditions"> <input tabindex="0" aria-required="true" type="checkbox" id="termsAndConditions"> <span> I agree to the <a href="http://www.example.com"> Terms & Conditions </a> . </span> </label> | |
Select elements | 4.4.1.1.3. Label: "Country" with Select Element<label for="selectCountry"> Country <abbr title="mandatory field"> *</abbr> </label> [...] <select tabindex="0" id="selectCountry"> <optgroup label="Cancel"> <option> Please select </option> </optgroup> [...] <optgroup label="Europe"> [...] <option value="BE"> <span> Belgium </span> </option> [...] </optgroup> [...] </select> |
An alternative in text form should be provided for <button>
elements through its text, the aria-label
or the title
attribute.
To prevent major automatic changes of content by changing the setting of a <button>
element, a text description on how content will change can be provided.
New windows, tabs or pop-ups should only open from buttons when necessary while giving an advanced warning before doing so.
Changes according to Error Handling should be applied.
Example | Illustration | Code |
---|---|---|
Buttons | 4.5.1.1.1. Button: "Sign In"<button tabindex="0" type="submit"> Sign In </button> | |
Button that opens a new window | This is a fictitious button that is not present in our Intershop Progressive Web App. | 4.5.1.1.2. Button: "Pay now (opens a new window)"<button tabindex="0" type="submit" onclick="window.open('http://www.example.com');"> Pay now (opens a new window) </button> |
Button that opens a pop-up (via CSS) | 4.5.1.1.3. Button: "Add to order template (opens a pop-up)"<button tabindex="0" title="Add to order template (opens a pop-up)" type="button"> [...] <svg aria-labelledby="AddTemplate" role="img"> <title id="AddTemplate">Add to order template (opens a pop-up)</title> [...] </svg> [...] </button> |
An alternative in text form should be provided for breadcrumb trails through the aria-label
attribute.
Example | Illustration | Code |
---|---|---|
Breadcrumb trail | 4.6.1.1.1. Link: "Skip the breadcrumb trail"<a class="skip" tabindex="0" href="#skipBreadcrumb"> Skip the breadcrumb trail </a> 4.6.1.1.2. Breadcrumb Trail<nav aria-label="Breadcrumb trail"> <ol> <li"> <a tabindex="0" href="http://www.example.com"> Home </a> <span"> / </span> </li> [...] </ol> </nav> [...] <span id="skipBreadcrumb"></span> Additional CSS code is necessary to hide the "Skip the navigation" link (see example code in section Tabbing through Components). |
Since some disabilities can interfere with the free movement of customers, complex gestures are often not performable. However, some components of an online shop, such as a product carousel, may require them. To make it possible for these customers to still use the components, all functionality that uses path-based or multipoint gestures should additionally be operable with a single pointer.
A path-based gesture is a gesture, where not only the endpoints of a gesture, but also at least one intermediate point affects the meaning. A common use case for them are product carousels. A multipoint gesture is performed by more than one pointer. For example, two fingers on a touchscreen could be required to zoom into specific content.
Example | Illustration | Code |
---|---|---|
Product carousel with alternative buttons to the left and right (arrows) | 5.1.1.1.1. Product Carousel<div aria-label="Product carousel"> <button tabindex="0" aria-label="Previous" type="button">Previous</button> [...] <button tabindex="0" aria-label="Next" type="button">Next</button> </div> |
For single pointer functionality, it is advisable that the completion of any part of the function is executed on the up-event and a mechanism is available to abort the function before its completion or to undo it after completion. Another solution can be that the up-event reverses any outcome of the preceding down event.
Additionally, it is advisable that touch events are only triggered when the touch is removed from a control and drag-and-drop functionalities can be cancelled.
The accessibility of external user interfaces can usually not be influenced; some may be accessible, others may be less accessible. For example, for payment methods, it is advisable to offer at least some that are accessible and not redirected to another page (e.g. credit card, invoice, cash on delivery, cash in advance etc.) since even if an online shop might be fully accessible on its own, a customer might not be able to complete an order because the available payment methods are not. It is also helpful to highlight those that are especially accessible.
Many people with a specific disability might not be able to perceive or complete a specific CAPTCHA task. Therefore, it is advisable to provide a text alternative for the CAPTCHA that informs the user about what he is supposed to do. Additionally, at least one alternative CAPTCHA with a different modality should be provided (e.g. an alternative audio task to a visual task). This is already the case for "reCAPTCHA v2". However, "reCAPTCHA v3" replaces all captcha tasks of the previous versions. Instead, it gathers the necessary information in the background while customers use the online shop without being interrupted. In rare cases, if the CAPTCHA algorithm ("reCAPTCHA v2") falsely claims that a bot uses the webpage, the customer is redirected to an accessible CAPTCHA task. With "reCAPTCHA v3", it is possible to specify what should happen if a suspected bot uses the online shop.
Example | Illustration |
---|---|
"I'm not a robot" checkbox (reCAPTCHA v2) | |
Invisible reCAPTCHA badge (reCAPTCHA v2 + reCAPTCHA v3) |
There are many possible ways to test if an online shop is accessible. To find major barriers of an online shop, it is already helpful to use browser extensions for a voice control and a screen reader and try to navigate through the online shop using only the keyboard and without looking at the screen . Additionally, there are many useful websites (e.g. https://www.webaccessibility.com/ ) and browser add-ons (e.g. Siteimprove Accessibility Checker ) to check the accessibility of a webpage and get some useful hints about missing accessibility. Finally, if a detailed analysis of barriers is required, it is advisable to have the online shop tested and analyzed by experts. The results are very detailed and can be used to significantly improve the accessibility of an online shop .
This is a short test case list that can be used to test the accessibility of the online shop. Use a screen reader (such as ChromeVox , JAWS , NVDA or VoiceOver ) and only a keyboard for the following scenarios:
Register a new customer.
Login with a customer.
Open a product detail page of a product via product search result.
Open a product detail page of a product via navigation.
Use a screen reader and check the output of the main content of a page (e.g. product detail page). Are all important information covered?
Add a product to a cart.
Place an order and check all order process pages (addresses, shipping, payment (redirect before and after checkout), review, receipt).
Check the order in your account section.
An extended test would be to use voice control instead of a keyboard.