CHAPTER 4 OF 5
4. Build a working disclosure
Your goal: connect a native button to content and keep its state accurate.
Try the control
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.
Connect state, relationship, and visibility
aria-expanded reports the state. We include aria-controls to reference the panel. JavaScript updates the state and the HTML hidden property together. The relationship attribute alone does not toggle anything.
<!-- 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>
The initial markup reports expanded content and keeps the inactive button hidden. After its click listener is ready, the script collapses the panel, sets aria-expanded="false", and reveals the button.
function setOpen(open) { panel.hidden = !open; button.setAttribute("aria-expanded", String(open)); }