Categories
Can generative AI help write accessible code?
Posted on by Léonie Watson in Design and development
Like many inventions before it, Generative AI is changing the way we do things. Like those inventions that went before it, Generative AI is capable of great good and great harm, and like the humans that used those inventions before us, we need to be smart about the way we use Generative AI.
One thing Generative AI is being used for is to assist with writing, or generating, code. There are many reasons why you might use Generative AI to do this; for example to rapidly create a disposable prototype, to jog your memory about a forgotten technique, or to help you produce code that utilises concepts you're not familiar with.
It's the last of those use cases we need to be smartest about, especially when it comes to using Generative AI to assist with producing accessible code. Even as they caution that they can make mistakes and urge you to check the facts, they're presenting you with seemingly authoritative answers to your questions. When the reason you're asking those questions is because you don't know the answers yourself, there is a risk that you won't know what a reliable answer looks like or how to verify it.
To illustrate this we asked three free Generative AI tools some typical questions about accessible code. The tools were Bard (now Gemini) from Google and ChatGPT 3.5 from OpenAI, as representatives of tools trained on general data, and Fix My Code (beta) from Userway as a tool specifically "trained in digital accessibility".
Note: generative AI tools do not give the same answer to the same question every time they're asked, so the responses included in this post are the ones we received the first time we asked.
Question 1
With production teams regularly aiming to make their products and services conform with the Web Content Accessibility Guidelines (WCAG), developers may turn to generative AI to find out if the code they've written is accessible. We asked Bard, ChatGPT, and Fix My Code:
Does this meet WCAG 2.2 Level AA?
<p>It was reported today that a dog ate my homework. <a href="news.html" aria-label="Dog ate my homework">Read more...</a> </p>
In this case the code does not conform to WCAG. It fails Success Criterion (SC) 2.5.3 Label in Name, which states:
For user interface components with labels that include text or images of text, the name contains the text that is presented visually.
In the code example above, the visible link text is "Read more...". The link's label, or name, is "Dog ate my homework". In other words, the link's name does not contain its visible text, and so the code fails SC 2.5.3 Label in Name.
Bard's response
I can't definitively tell you whether your snippet meets WCAG 2.2 Level AA based on the information provided. Determining WCAG compliance requires a thorough evaluation of all aspects of the content and code, including:
Bard's full response
- Text alternatives for the image: Is there a way for users who can't see the image to access the information it conveys? This could be done through a >* descriptive alt text attribute or caption.
- Color contrast: Does the text have sufficient contrast against the background to be readable for people with visual impairments? Tools like WebAIM's Color Contrast Checker can help you evaluate this.
- Keyboard accessibility: Can users navigate the content and link using only the keyboard? This includes ensuring focus indicators are visible and keyboard-operable components function correctly. Code:
- Semantic HTML: Is the HTML structure meaningful and conveys the content's purpose? For example, using heading tags appropriately and ARIA attributes where necessary.
- Valid code: Is the HTML and CSS code free of syntax errors that could affect accessibility? Additionally:
- Level of compliance: While the snippet mentions Level AA, WCAG compliance requires meeting all success criteria under that level, not just some. Therefore, a complete analysis of the content and code, preferably using automated testing tools and manual checking, is required to determine WCAG 2.2 Level AA compliance.
Here are some resources that can help you further:
Bard makes it clear it cannot answer the question. It goes on to offer some general but reliable guidance, and then recommends some further resources to help you learn about the topic.
ChatGPT's response
The provided HTML code snippet does not fully conform to WCAG 2.2 Level AA accessibility standards. While it includes some accessibility features, there are a few issues:
ChatGPT's full response
- ARIA Label: You have used an
aria-label
attribute to provide an accessible label for the link. This is a good practice. However, the label "Dog ate my homework" should be more informative. It's better to provide a more descriptive label that explains the purpose or content of the link. For example, you could use something like "Read more about the homework incident" for better clarity.- Text Content: The link "Read more..." does not provide meaningful context or information about the linked content. It's recommended to use descriptive link text that makes sense on its own, without relying solely on the context provided by the surrounding text. This helps users with screen readers or those who navigate using keyboard shortcuts. Here's an improved version of the code:
<p>It was reported today that a dog ate my homework. <a href="news.html" aria-label="Read more about the homework incident">Read more about the homework incident</a> </p>
In this updated code, the link text is more descriptive, and the
aria-label
attribute is also more informative, making it closer to meeting WCAG 2.2 Level AA standards. However, it's essential to consider other aspects of accessibility, such as contrast ratios, keyboard navigation, and semantic HTML elements, when designing web content to ensure full compliance with accessibility standards.
ChatGPT answers the question correctly, but for the wrong reasons. It misses the fact the code snippet fails SC 2.5.3 Label in Name and instead focuses on the quality of the label and link text. Had the question specified WCAG Level AAA instead of Level AA, ChatGPT would be correct about the link text because the Level AAA SC 2.4.9 Link Purpose (Link Only) requires that the purpose of a link can be "identified from link text alone". However, the Level A SC 2.4.4 Link Purpose in Context has more latitude, requiring that the purpose of a link "can be determined from the link text alone or from the link text together with its programmatically determined link context".
Despite the misinformation, the improved code snippet ChatGPT provides is more accessible than the original. It meets SC 2.4.9, as well as SC 2.4.4. Interestingly, it also meets SC 2.5.3, albeit inadvertently. By making the value of the -aria-label
attribute and the link text identical, the visible link text is included in the link's name.
Note: by making the visible link text and the value of the aria-label
attribute identical it makes the aria-label
attribute redundant and should ideally have removed it as a matter of general ARIA best practice.
Fix My Code's response
Yes, the code provided does adhere to the WCAG 2.2 Level AA standards.
Fix My Code's full response
The anchor tag
<a href="news.html" aria-label="Dog ate my homework">Read more...</a>
includes anaria-label
attribute which provides a concise description of the link's purpose. This complies with the WCAG Guideline 2.4.4 (Link Purpose in Context), which states that the purpose of each link should be able to be determined from the link text alone, or from the link text together with its programmatically determined link context. But a small improvement can be made by making the link more descriptive:<p>It was reported today that a dog ate my homework. <a href="news.html" aria-label="Read more about the dog that ate my homework">Read more...</a>
Here, the 'aria-label' "Read more about the dog ate my homework" provides a more detailed context to users using assistive technologies like screen readers.
Despite being trained in digital accessibility, Fix My Code does not answer the question correctly. It rightly states that the code snippet passes SC 2.4.4 Link Purpose in Context, but misses the fact it fails SC 2.5.3 Label in Name. However, as with the response from ChatGPT, the code snippet provided by Fix My Code does, inadvertently, pass SC 2.5.3 Label in Name by including the phrase "Read more" (the visible link text) within the value of the aria-label
attribute.
Question 2
Although there are documented accessible patterns for most common components, they remain notoriously difficult to get right. That of course makes them exactly the kind of thing someone might ask a generative AI tool to help with. We asked Bard, ChatGPT, and Fix My Code:
Give me all the code I need to create a set of accessible tabs for a website.
The definitive pattern for a set of accessible tabs is the one found in the W3C ARIA Authoring Practices. It has all the expected semantic information and functionality of a set of tabs, with support for mouse, keyboard (with or without a screen reader),and touch.
Bard's response
Here's the code you need to create accessible tabs for a website, incorporating HTML, CSS, and JavaScript:
Bard's full response
HTML:
<div class="tabs"> <ul class="tab-list" role="tablist"> <li class="tab active" role="tab" aria-selected="true" aria-controls="tab1"> <button>Tab 1</button> </li> <li class="tab" role="tab" aria-selected="false" aria-controls="tab2"> <button>Tab 2</button> </li> <li class="tab" role="tab" aria-selected="false" aria-controls="tab3"> <button>Tab 3</button> </li> </ul> <div class="tab-panels"> <div id="tab1" class="tab-panel active" role="tabpanel" aria-labelledby="tab1-button"> </div> <div id="tab2" class="tab-panel" role="tabpanel" aria-labelledby="tab2-button"> </div> <div id="tab3" class="tab-panel" role="tabpanel" aria-labelledby="tab3-button"> </div> </div> </div>
Use code with caution.
CSS:
CSS.tabs { /* Basic styling for the tabs container */ } .tab-list { /* Styling for the list of tabs */ display: flex; list-style: none; margin: 0; padding: 0; } .tab { /* Styling for individual tabs */ padding: 10px 20px; border: 1px solid #ccc; cursor: pointer; } .tab button { /* Remove default button styling */ background: none; border: none; padding: 0; cursor: pointer; } .tab.active { /* Styling for the active tab */ background-color: #eee; border-bottom: none; } .tab-panels { /* Styling for the tab content container */ } .tab-panel { /* Styling for individual tab content panels */ display: none; padding: 20px; } .tab-panel.active { /* Styling for the visible tab panel */ display: block; }
Use code with caution.
JavaScript:
const tabs = document.querySelectorAll('.tab'); const tabPanels = document.querySelectorAll('.tab-panel'); tabs.forEach(tab => { tab.addEventListener('click', () => { // Deactivate all tabs tabs.forEach(tab => tab.classList.remove('active')); // Hide all tab panels tabPanels.forEach(panel => panel.classList.remove('active')); // Activate the clicked tab tab.classList.add('active'); / Show the corresponding tab panel const panelId = tab.getAttribute('aria-controls'); document.getElementById(panelId).classList.add('active'); }); );
Use code with caution.
Accessibility features:
- ARIA attributes: role, aria-selected, aria-controls, aria-labelledby
- Keyboard navigation: Tabs can be focused and activated using the Tab key and Space or Enter keys.
- Focus management: Focus moves to the corresponding tab panel when a tab is activated.
- Visual indicators: Active tabs have distinct visual styles for clarity. Additional considerations:
- Use clear and descriptive tab labels for easy understanding.
- Test the tabs with assistive technologies like screen readers to ensure proper functionality.
- Consider using a JavaScript framework or library for more advanced tab features and easier management.
The code provided by Bard has several flaws. The most notable is that it does not have the expected keyboard support. The expectation is that the Up/Down/Left/Right Arrow keys can be used to cycle through each of the tabs. The focus management mentioned in Bard's response is also not present.
The expected ARIA roles and properties have been included by Bard, but not always correctly. For example:
- Each tab consists of a
<li>
with a<button>
element inside it. The ARIA propertiesrole=tab
,aria-controls
, andaria-selected
, have been applied to the<li>
element but should be applied to the<button>
element instead (androle="none"
applied to the<li>
) - The value of the
aria-selected
attribute on each<li>
is not updated as a new tab is selected, meaning screen readers always announce that "Tab 1" is selected even when it is not the currently selected Tab - The
aria-labelledby
attribute on each of the<div>
elements withrole="tabpanel"
points to a non-existentid
, meaning that the tabpanels do not have an accessible name that's announced by screen readers
ChatGPT's response
Creating accessible tabs for a website involves HTML, CSS, and JavaScript code. Here's a simple example of how you can create accessible tabs:
ChatGPT's full response
HTML:
<html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> title>Accessible Tabs</title> link rel="stylesheet" href="styles.css"> /head> <body> div class="tabs"> <button class="tab-button" data-tab="tab1">Tab 1</button> <button class="tab-button" data-tab="tab2">Tab 2</button> <button class="tab-button" data-tab="tab3">Tab 3</button> </div> <div id="tab1" class="tab-content"> <h2>Tab 1 Content</h2> <p>This is the content of Tab 1.</p> </div> <div id="tab2" class="tab-content"> <h2>Tab 2 Content</h2> <p>This is the content of Tab 2.</p> </div> <div id="tab3" class="tab-content"> <h2>Tab 3 Content</h2> <p>This is the content of Tab 3.</p> </div> <script src="script.js"></script> </body> </html>
CSS (styles.css):
/* Style for tabs and tab content */ .tabs { display: flex; } .tab-button { background-color: #f1f1f1; border: none; padding: 10px 20px; cursor: pointer; } .tab-button:focus { outline: 2px solid #007bff; } .tab-content { display: none; padding: 20px; } /* Show the first tab by default */ .tab-content:first-child { display: block; }
JavaScript (script.js):
//Get all tab buttons and tab content const tabButtons = document.querySelectorAll('.tab-button'); const tabContents = document.querySelectorAll('.tab-content'); // Add click event listeners to tab buttons tabButtons.forEach(button => { button.addEventListener('click', () => { // Hide all tab content tabContents.forEach(content => { content.style.display = 'none'; }); // Show the selected tab content const tabId = button.getAttribute('data-tab'); const tabContent = document.getElementById(tabId); if (tabContent) { tabContent.style.display = 'block'; } // Set the focus to the selected button for accessibility button.focus(); }); });
This code sets up three tabs and their corresponding content. When you click on a tab button, it will display the associated content and hide the others. It also manages keyboard focus for accessibility. You can customize the styles in the CSS and add more tabs as needed.
The code provided by ChatGPT does not include any ARIA. This means that screen reader users are not informed that it's a set of tabs, even though they're styled to look like them.
For the same reason, screen reader users have no way to tell which is the currently selected tab, other than by navigating through the content to find out. Although sighted people may be able to tell from the visible content which tab is currently selected, the design of the currently selected tab (button" is indistinguishable from the others. This may be problematic for anyone using screen magnification or a small screen device.
The structure and relationship between the tabs may be apparent visually, but there is no programmatic structure or relationships to help anyone who is unable to see the tabs on-screen.
Fix My Code's response
You can create a set of accessible tabs in compliance with the WCAG guidelines using HTML, CSS, and JavaScript. Here's a production-ready code snippet:
Fix My Code's full response
<!DOCTYPE html> <html> <head> <title>Accessible Tabs</title> <style> .tab-content { display: none; } .tab-button:focus, .tab-button[aria-selected="true"] { border-color: blue; } </style> </head> <body> <div id="tabs" role="tablist"> <button id="tab1" class="tab-button" role="tab" aria-controls="panel1" aria-selected="true">Tab 1</button> <button id="tab2" class="tab-button" role="tab" aria-controls="panel2" aria-selected="false">Tab 2</button> <button id="tab3" class="tab-button" role="tab" aria-controls="panel3" aria-selected="false">Tab 3</button> </div> <div id="panel1" class="tab-content" role="tabpanel" aria-labelledby="tab1">Content for Tab 1</div> <div id="panel2" class="tab-content" role="tabpanel" aria-labelledby="tab2">Content for Tab 2</div> <div id="panel3" class="tab-content" role="tabpanel" aria-labelledby="tab3">Content for Tab 3</div> <script> var tabs = document.querySelectorAll('[role="tab"]'); // Assign each tab an unique accessible name and controls tabs.forEach(function(tab, i) { tab.setAttribute('aria-controls', 'panel' + (i+1)); tab.setAttribute('aria-labelledby', 'tab' + (i+1)); }); // Assign tab behavior tabs.forEach(function(tab) { tab.addEventListener('click', changeSelectedTab); }); // Change the selected tab and unselect others function changeSelectedTab(e) { tabs.forEach(function(tab) { tab.setAttribute('aria-selected', 'false'); }); this.setAttribute('aria-selected', 'true'); let controls = this.getAttribute('aria-controls'); document.getElementById(controls).style.display = 'block'; let allTabContent = document.querySelectorAll('.tab-content'); allTabContent.forEach(function(tab_content) { if(tab_content.id != controls) { tab_content.style.display = 'none'; } }); } </script> </body> </html>
Let's explain how this snippet follows the WCAG Guidelines:
- The HTML structure of this set of tabs employs ARIA roles such as "tablist", "tabpanel", and "tab" to give assistive technologies a better understanding of the content's structure.
- The CSS adds a visual focus style to the tab that is currently selected or in focus.
- The JavaScript adds functionality to the tab buttons. When a button is clicked, it sets its aria-selected attribute to "true" and all others to "false". It also hides the content of all tabs and reveals the current tab's content.
- The 'aria-controls' and 'aria-labelledby' attributes pair tabs correctly with their content panels, enhancing the screen reader's navigation. By following these guidelines, you provide an accessible tab control that can be properly understood and manipulated with a keyboard and a screen reader.
The code provided by Fix My Code uses ARIA roles and properties correctly, but it does not have the expected keyboard support and so is not "in compliance with the W3C guidelines" as stated because it fails SC 2.1.1 Keyboard.
The absence of keyboard support is particularly problematic for Windows screen readers that use "applications mode" to facilitate keyboard interaction for custom components like a set of tabs. When the Enter or Space key is used to activate one of the tabs, these screen readers automatically switch into applications mode and expect the custom component to handle all the keyboard interaction. When the keyboard support is not provided, the screen reader user must manually exit applications mode before they're able to interact with anything else.
Be smart about generative AI
In October 2023, the TetraLogical team asked the question "Can generative AI help me write accessible code?". The answer was surprisingly simple - yes, if you're smart enough not to trust every answer you receive and you know how to verify its accuracy.
That's a very big "if" however.
Expecting people not to use generative AI tools to help them write accessible code is pointless. People will always use tools to help improve their knowledge and/or productivity - we've been using search engines for precisely this purpose for decades after all.
So how do we get smart about using generative AI tools to help us write accessible code?
You can start by always asking yourself two questions:
- How transparent is the tool about its limitations?
- How can I verify the answer I've received?
To answer these guard-rail questions, start by looking for general statements that tell you how reliable the generative AI's responses are.
Bard and ChatGPT both make it clear that they're capable of getting things wrong and that you should check their answers:
Bard may display inaccurate info, including about people, so double-check its responses.
ChatGPT can make mistakes. Consider checking important information.
Then consider the response itself. Bard's response to the question about WCAG conformance is helpful in this regard. It not only admits it cannot answer the question, it makes no attempt to do so either. What it does do is provide links to resources you can use to verify the credibility of the general guidance it offers instead.
It could be argued that Bard's response to the WCAG question was the least helpful, but it is perhaps the most trustworthy of all the responses received because the information it provides is accurate and verifiable.
Conversely, the response from Fix My Code appears absolutely authoritative, and it does not have a general statement that acknowledges its potential fallibility.
This theme continues with the responses to the question about an accessible set of tabs, but this time Bard and ChatGPT also give authoritative seeming answers, as well as Fix My Code, despite all three responses having at least one major accessibility flaw.
Other than the generic statements about the need to check its responses, none of these generative AI tools gives any hint that their answers may not be correct or provides any recommended resources for checking - though Bard does note that you should "Test the tabs with assistive technologies like screen readers to ensure proper functionality".
The unavoidable conclusion is that when you ask a generative AI tool for help writing accessible code, is that you should not trust the response you get and should verify it with sources you do trust.
This is not to say that you shouldn't use generative AI tools for help writing accessible code. You just need to be smart about it when you do. Ask yourself the two questions mentioned before and act accordingly. If you have accessibility knowledge, review the code carefully and fix the issues it will likely include. If you don't, assume the response is incorrect and incomplete and verify it using trusted sources.
Next steps
Find out more about inclusive XR: accessible 3D experiences or browse our training classes, training courses, and training programmes.
We like to listen
Wherever you are in your accessibility journey, get in touch if you have a project or idea.