Error - Broken ARIA reference
- WAVE Category: Error
- WAVE Error: Broken ARIA reference
Page Content Status
- WAVE Error guidance text
- Example of a DOM Snippet Generated From WAVE Tool
- Project Team Error guidance
- Project Team Page Details
- Project Team Issue and PR details
- Project Team Solution
- Credits/Authors
WAVE Error guidance
Click to see the WAVE Tool's Reference material on Broken ARIA reference. These guidelines may not specifically address your problem, but they might provide context for getting started.
Click to see WAVE Tool Reference
This content added 2026-02-26. Check for updated guidance at: https://wave.webaim.org/api/docs?format=html#aria_reference_broken
WAVE Category
Error
WAVE Error
Broken ARIA reference
What It Means
An aria-labelledby or aria-describedby reference exists, but the target for the reference does not exist.
Why It Matters
ARIA labels and descriptions will not be presented if the element referenced does not exist in the page.
What To Do
Ensure the element referenced in the aria-labelledby or aria-describedby attribute value is present within the page and presents a proper label or description.
The Algorithm... in English
An element has an aria-labelledby or aria-describedby value that does not match the id attribute value of another element in the page.
Standards and Guidelines
Example of a DOM Snippet Generated From WAVE Tool
Project Team Error guidance
The following material covers how the TDM team has provided a solution to the Broken Aria reference WAVE error.
Project Page Details
- TDM Page name: ALL
- TDM Staging URL: N/A
- Required User Role: ALL
Project Team Issue and PR details
Related GitHub Issue, PR and React Component 1
- Related GitHub Issue 1:
- https://github.com/hackforla/tdm-calculator/issues/2531
- Related Pull Request:
- https://github.com/hackforla/tdm-calculator/pull/2786
- follow-up commit: https://github.com/hackforla/tdm-calculator/commit/16072c43a7aa7d18656d133c302190d5a1e53ee9
- https://github.com/hackforla/tdm-calculator/pull/2786
- React Component:
client/src/components/Layout/NavBarLogin.jsx
Project Team Solution
What is the specific problem that was occurring?
Dismissing the NavBarLogin's popup removes the Popup component from the DOM, but it does not also purge the aria-describedby="popup-1" attribute from NavBarLogin. At that point, a screen reader may attempt to process the DOM element with id="popup-1", which no longer exists, leading to the “Broken ARIA reference” error.
Note: failure to remove aria-describedby is a bug in the react-js-popup open source project.
What is the proposed solution to this problem?
There are two workable patterns, depending on how reusable you need the fix to be:
1) One-off fix (component-specific): Augment the popup “close” handler so it removes the stray aria-describedby from the trigger element when the popup unmounts.
2) Reusable fix (recommended): Use a small custom hook (useReplaceAriaAttribute) to reliably remove the incorrect ARIA attribute added by reactjs-popup (typically aria-describedby) and replace it with a safer attribute (often aria-controls) that references a stable element ID you control.
This hook-based approach exists specifically because reactjs-popup may clone the trigger and strip attributes, making it difficult to set or override ARIA props directly on the trigger, and DOM manipulation can be subject to race conditions.
Step-By-Step Guide
Click to see step-by-step guide
Option A: Component-specific fix (simple + local)
- Add a stable
idto the trigger wrapper (the element that ends up receivingaria-describedby). - In the popup
onClosehandler, callremoveAttribute("aria-describedby")on that element before unmount completes.
Option B: Reusable fix using useReplaceAriaAttribute (recommended)
Step 1: Import the hook
Step 2: Add a unique id to your target element
Recommended pattern (avoids mismatches):
Use it on the element:
Step 3: Call the hook in your component
useReplaceAriaAttribute({
elementId,
deps: [projectRules],
attrToRemove: "aria-describedby",
attrToAdd: "aria-controls",
value: `popup-content-${elementId}`,
});
Step 4 (optional but common): Ensure the referenced element exists
If you set aria-controls to some id, ensure the popup content uses that id:
Dependency array tips
Include in deps anything that could change whether:
- the trigger exists / is re-rendered
- the element id changes
- the popup structure changes
Other Technical Details
Click to see other technical details
reactjs-popupclones the trigger element and strips any attributes, which can prevent setting ARIA attributes or refs directly on the trigger.- Ad-hoc DOM manipulation can be subject to timing/race issues if you do it in many components.
- A hook centralizes the workaround so components don’t each reinvent their own “removeAttribute” logic.
Reference (discussion): https://github.com/hackforla/tdm-calculator/issues/2410#issuecomment-3561030939
Code Snippet With Solution
Click to see code snippets
Component: NavBarLogin.jsx
const NavBarLogin = ({ ... }) => {
const closeModal = () => { // 1. augment closeModal handler by removing `aria-describedby` attribute
const loginLink = document.getElementById("login-link");
loginLink.removeAttribute("aria-describedby");
setTooltipOpen(false);
};
return (
<Popup>
<!-- ... -->
onClose={closeModal}
trigger={(
<!-- 2. add a unique id, "login-link" to the span that will (eventually) receive the `aria-describedby` attribute
<span id="login-link" style={{ cursor: "pointer" }}>
{loginLink}
</span>
)}
/> <!-- end Popup -->
) // end-return
Reusable hook approach (useReplaceAriaAttribute)
import { useReplaceAriaAttribute } from "hooks/useReplaceAriaAttribute";
import Popup from "reactjs-popup";
function MyComponent({ project }) {
const elementId = `context-menu-button-${project.id}`;
const popupContentId = `popup-content-${elementId}`;
useReplaceAriaAttribute({
elementId,
deps: [projectRules],
attrToRemove: "aria-describedby",
attrToAdd: "aria-controls",
value: popupContentId,
});
return (
<Popup
trigger={
<button id={elementId} aria-label="context menu button">
Menu
</button>
}
>
<div id={popupContentId}>
{/* Menu items */}
</div>
</Popup>
);
}
Why the Fix Works
By deleting (or replacing) the aria-describedby attribute when the modal is closed or when the trigger is rendered, assistive tech will no longer associate the trigger with a non-existent tooltip/popup element. This prevents the “broken ARIA reference” error and avoids confusing behavior in screen readers.
Where this solution is applicable
- Pop-ups, modals, and UI that can be dismissed/unmounted by users
- Anytime an HTML element gets removed from the DOM by a user interaction
- Particularly relevant for libraries like
reactjs-popupthat may inject ARIA attributes and/or clone/strip trigger props
Screenshots of WAVE Error
1.01 Login Popup
1.A Login Popup, Screenshot
1.B Login Popup, Screenshot of WAVE Annotations
- this signifies that this HTML node contains an attribute,
role="tooltip"(unrelated to the WAVE error in the Action Items)
1.C Login Popup, Screenshot of HTML
- note the attribute,
id="popup-1"
2. Login Link
2.A Login Link, Screenshot
2.B Login Link, Screenshot of WAVE Annotations
- Styles were removed to adequately show the WAVE annotation for the Login link
- the WAVE Tool can disrupt styles in order to display the info sidebar, which effectively hid the Login link
2.C Login Link, Screenshot of HTML
- note the attribute,
aria-describedby="popup-1"
Credits/Authors
All HfLA members who contributed to this wiki page and/or contributed to Pull Requests that provided solutions for this page.
- @ryanfchase
- @Tony-Villa
- @Rabia2219