ARIA / COMPLETE EXAMPLE
Complete commented example
The markup and the behavior, together.
Open the example separately, or expand the source blocks below. Keep aria-demo.js beside the HTML when adapting the interaction. The stylesheets provide its appearance; the interaction does not depend on them. If the script is unavailable, the workshop details remain readable. Update the example's navigation links for your own site.
Try it: activate Workshop details with a pointer, Enter, or Space. Tab into the revealed link, then move back to the button to close it.
What to bring
Bring a laptop if you have one. Printed materials are also available.
Read the complete commented HTML
<!doctype html> <!-- Project: Common Ground Accessibility Author: Laurie Kamrowski-Fick Last updated: November 1, 2026 Copyright © 2026 Common Ground Accessibility --> <html lang="en"> <!-- SETTINGS: Identify the example, preserve zoom, and load the optional controller after parsing. --> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="author" content="Laurie Kamrowski-Fick"> <title>ARIA disclosure example | Common Ground Accessibility</title> <link rel="stylesheet" href="styles.css?v=20261101-guides"> <link rel="stylesheet" href="guides.css?v=20261101-guides-final"> <script src="aria-demo.js?v=20261101-aria" defer></script> </head> <body> <!-- CONTENT: The instructional example retains readable information if its enhancement is unavailable. --> <main class="container guide-article guides-intro"> <h1>Workshop information</h1> <p>Try the button with a pointer, Enter, or Space. The content remains available if JavaScript is disabled.</p> <!-- FALLBACK: The content is visible without JavaScript; the inactive control stays hidden. --> <div data-aria-disclosure> <!-- BUTTON: Native keyboard behavior and visible text supply the role and accessible name. --> <button type="button" class="button" aria-expanded="true" aria-controls="workshop-details" hidden>Workshop details</button> <!-- RELATIONSHIP: The ID matches aria-controls; the attribute itself does not hide or show this panel. --> <div id="workshop-details"> <h2>What to bring</h2> <p>Bring a laptop if you have one. Printed materials are also available.</p> <p><a href="/guides.html">Browse all accessibility guides</a></p> </div> </div> <p><a href="/aria-guide-code.html">Return to the commented example</a></p> </main> <!-- ATTRIBUTION: Preserve authorship when studying or adapting the example. --> <footer><div class="container"><p>© 2026 Common Ground Accessibility. Educational example.</p></div></footer> </body> </html>
Read the complete commented JavaScript
// Project: Common Ground Accessibility // Author: Laurie Kamrowski-Fick // Copyright © 2026 Common Ground Accessibility // A small instructional disclosure using a native button, an explicit relationship, and synchronized state. (() => { 'use strict'; document.querySelectorAll('[data-aria-disclosure]').forEach(example => { const button = example.querySelector('button[aria-controls]'); if (!button) return; const panel = document.getElementById(button.getAttribute('aria-controls')); // An incomplete example must retain readable fallback content, rather than expose a dead control. if (!panel || !example.contains(panel)) return; function setOpen(open) { // Visibility and the exposed expanded state always change together. panel.hidden = !open; button.setAttribute('aria-expanded', String(open)); } // Native buttons already translate Enter and Space into clicks; do not add duplicate key handlers. button.addEventListener('click', () => { setOpen(button.getAttribute('aria-expanded') !== 'true'); }); // HTML starts with readable content and a hidden button. Enhance only after its listener is ready. setOpen(false); button.hidden = false; }); })();
For a simpler implementation, return to Chapter 1’s native details example. For the reasoning behind this controller, revisit Chapter 4.