From ea42bfc6e5437c5d6ed0d78dff23f94f37583ef1 Mon Sep 17 00:00:00 2001 From: shivanissingh Date: Thu, 6 Nov 2025 16:34:04 +0530 Subject: [PATCH] feat(curriculum): Add interactive examples to aria-expanded attribute lesson (#63554) --- .../672a54ce90c19e9038f481d7.md | 123 +++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/curriculum/challenges/english/blocks/lecture-understanding-aria-expanded-aria-live-and-common-aria-states/672a54ce90c19e9038f481d7.md b/curriculum/challenges/english/blocks/lecture-understanding-aria-expanded-aria-live-and-common-aria-states/672a54ce90c19e9038f481d7.md index 4464daed19f..09dc951b60b 100644 --- a/curriculum/challenges/english/blocks/lecture-understanding-aria-expanded-aria-live-and-common-aria-states/672a54ce90c19e9038f481d7.md +++ b/curriculum/challenges/english/blocks/lecture-understanding-aria-expanded-aria-live-and-common-aria-states/672a54ce90c19e9038f481d7.md @@ -5,7 +5,7 @@ challengeType: 19 dashedName: what-is-the-aria-expanded-attribute --- -# --description-- +# --interactive-- The `aria-expanded` attribute is used for accessibility purposes to indicate if a control is expanded or collapsed. It's used in conjunction with collapsible widgets like menus, accordions and other disclosure widgets that control the visibility of content. The `aria-expanded` attribute is set to `true` if the control is expanded, or `false` if it is collapsed. @@ -35,19 +35,78 @@ Additionally, the properties, `aria-controls` and `aria-owns` can be used in com Let's start with `aria-controls`. When used with `aria-expanded`, the `aria-controls` attribute is used to specify the element being controlled. For example, a button might expand or collapse a list acting as a menu. The value of `aria-controls` will be the `id` of the controlled element (the menu list in this example). +:::interactive_editor + ```html + + + + ``` +```css +button { + background-color: #0078d4; + color: white; + padding: 8px 12px; + border: none; + border-radius: 4px; + cursor: pointer; +} + +button:hover { + background-color: #005ea2; +} + +ul { + list-style: none; + padding: 0; + margin: 8px 0 0; + border: 1px solid #ccc; + border-radius: 4px; + width: 150px; + background: #fff; +} + +li { + padding: 8px; + cursor: pointer; +} + +li:hover { + background-color: #f2f2f2; +} + +``` + +```js +const button = document.querySelector('button'); +const menu = document.getElementById(button.getAttribute('aria-controls')); + +button.addEventListener('click', () => { + const expanded = button.getAttribute('aria-expanded') === 'true'; + button.setAttribute('aria-expanded', String(!expanded)); + menu.hidden = expanded; +}); + +``` + +::: + Notice that the list immediately follows the controlling button. For expandable controls like this, it is best to have the expanded content immediately follow the element that controls it in the DOM. This prevents screen reader users from having to search for the expanded content, and makes it easier for keyboard users to navigate through any interactive controls in the expanded content. If it is not possible to place the expanded content immediately after the controlling element, the `aria-owns` attribute allows you to virtually move it after the control in the accessibility tree. This allows assistive technology like screen readers to pretend the expanded content is placed directly after the control in the DOM. +:::interactive_editor + ```html + +
@@ -56,8 +115,70 @@ If it is not possible to place the expanded content immediately after the contro
  • ...
  • ...
  • + + ``` +```css +button { + background-color: #0078d4; + color: white; + padding: 8px 12px; + border: none; + border-radius: 4px; + cursor: pointer; + margin-bottom: 10px; +} + +button:hover { + background-color: #005ea2; +} + +ul { + list-style: none; + padding: 0; + margin: 8px 0; + border: 1px solid #ccc; + border-radius: 4px; + width: 150px; + background: #fff; + position: absolute; + z-index: 10; +} + +li { + padding: 8px; + cursor: pointer; +} + +li:hover { + background-color: #f2f2f2; +} + +``` + +```js +const button = document.querySelector('button'); +const menu = document.getElementById(button.getAttribute('aria-owns')); + +button.addEventListener('click', () => { + const expanded = button.getAttribute('aria-expanded') === 'true'; + button.setAttribute('aria-expanded', String(!expanded)); + menu.hidden = expanded; +}); + +// Close the menu if user clicks outside +document.addEventListener('click', (e) => { + if (!button.contains(e.target) && !menu.contains(e.target)) { + button.setAttribute('aria-expanded', 'false'); + menu.hidden = true; + } +}); + +``` + +::: + There are drawbacks to using the `aria-owns` attribute. It creates unnecessary verbosity for screen reader users since most screen readers will automatically read out the entire contents of the expanded element when first expanded. It also does not change the tab order, potentially forcing keyboard users to tab through all of the other interactive controls on the page before reaching the expanded content, unless you manage the tabbing order with JavaScript. Ideally, the expandable content should be placed after the control element, and the `aria-owns` attribute should only be used in a worst case scenario when that is not possible. If it must be used, you will need to thoroughly test with a wide range of screen readers and browsers to ensure that your implementation is accessible for everyone.