HTML Guides for aria-describedby
Learn how to identify and fix common HTML validation errors flagged by the W3C Validator — so your pages are standards-compliant and render correctly across every browser. Also check our Accessibility Guides.
The core problem is that aria-disabled="true" is purely an accessibility hint — it communicates a disabled state to assistive technologies like screen readers, but it has no effect on the actual behavior of the element. When an a element has an href attribute, the browser treats it as a valid hyperlink regardless of any ARIA attributes. Users can still click it, follow it via keyboard navigation, and navigate to its destination. This mismatch between the announced state (“disabled”) and actual behavior (“fully functional link”) creates a confusing and misleading experience, particularly for users of assistive technologies.
The W3C validator flags this combination because it violates the principle that ARIA states should accurately reflect an element’s true interactive state. A link that claims to be disabled but still works undermines user trust and can cause real usability problems.
Why this matters
- Accessibility: Screen readers will announce the link as disabled, but users who activate it will be unexpectedly navigated away. This is disorienting and violates WCAG guidance on predictable behavior.
- Standards compliance: The HTML specification and ARIA in HTML requirements discourage or disallow this combination because it produces an unreliable user experience.
- Browser behavior: No browser will disable a link just because aria-disabled="true" is present. The href attribute always makes the a element an active hyperlink.
How to fix it
You have two main approaches depending on your intent:
-
The link should be active: Remove aria-disabled="true" and keep the href. If the link works, don’t mark it as disabled.
-
The link should be disabled: Remove the href attribute. Without href, the a element becomes a placeholder link that is not interactive. You can then use aria-disabled="true" to communicate the disabled state, tabindex="-1" to remove it from the keyboard tab order, and CSS to style it as visually disabled. You should also add JavaScript to prevent activation if needed.
Examples
Incorrect
This triggers the validation error because aria-disabled="true" conflicts with the presence of href:
<a href="/dashboard" aria-disabled="true">Go to Dashboard</a>
Correct — Keep the link active
If the link should function normally, simply remove the aria-disabled attribute:
<a href="/dashboard">Go to Dashboard</a>
Correct — Disable the link
If the link should be non-actionable (e.g., a navigation item the user doesn’t currently have access to), remove the href attribute and use ARIA and CSS to communicate the disabled state:
<a aria-disabled="true" tabindex="-1" role="link" class="link-disabled">Go to Dashboard</a>
.link-disabled {
color: #6c757d;
cursor: not-allowed;
pointer-events: none;
text-decoration: none;
}
In this approach:
- Removing href ensures the link is not actionable by the browser.
- aria-disabled="true" tells assistive technologies the element is disabled.
- tabindex="-1" removes the element from the keyboard tab order so users can’t Tab to it.
- role="link" preserves the link semantics so screen readers still identify it as a link (an a without href loses its implicit link role).
- The CSS provides a visual indication that the element is disabled, with pointer-events: none preventing mouse clicks and cursor: not-allowed giving a visual cue on hover.
Correct — Use a button instead
If the “link” triggers an action rather than navigating somewhere, consider using a button element instead. Buttons natively support the disabled attribute:
<button type="button" disabled>Perform Action</button>
This is the simplest and most robust solution when the element doesn’t need to be a link. The disabled attribute is natively understood by browsers and assistive technologies without any ARIA workarounds.
The aria-describedby attribute is a core part of WAI-ARIA, the Web Accessibility Initiative’s specification for making web content more accessible. It works by creating a relationship between an element and one or more other elements that provide additional descriptive text. Screen readers and other assistive technologies use this relationship to announce the descriptive text when a user interacts with the element.
When you set aria-describedby="some-id", the browser looks for an element with id="some-id" in the same document. If no matching element exists, the reference is broken. This means assistive technologies cannot find the description, and the attribute silently does nothing. The W3C validator flags this as an error because a dangling reference indicates a bug — either the referenced element was removed, renamed, or was never added.
This issue commonly arises due to:
- Typos in the id value — the aria-describedby value doesn’t match the target element’s id exactly (the match is case-sensitive).
- Dynamic content — the described-by element is rendered conditionally or injected by JavaScript after validation.
- Copy-paste errors — markup was copied from another page or component, but the referenced element wasn’t included.
- Refactoring — an element’s id was changed or the element was removed, but the aria-describedby reference wasn’t updated.
Multiple id values can be listed in aria-describedby, separated by spaces. Every single id in that list must resolve to an element in the document. If even one is missing, the validator will report an error for that reference.
How to fix it
- Check for typos. Compare the value in aria-describedby against the id of the target element. Remember that id matching is case-sensitive — helpText and helptext are different.
- Add the missing element. If the descriptive element doesn’t exist yet, create it with the matching id.
- Remove stale references. If the description is no longer needed, remove the aria-describedby attribute entirely rather than leaving a broken reference.
- Verify all IDs in a multi-value list. If aria-describedby contains multiple space-separated IDs, confirm each one exists.
Examples
Broken reference (triggers the error)
In this example, aria-describedby points to password-help, but no element with that id exists in the document:
<label for="password">Password</label>
<input type="password" id="password" aria-describedby="password-help">
Fixed by adding the referenced element
Adding an element with id="password-help" resolves the issue:
<label for="password">Password</label>
<input type="password" id="password" aria-describedby="password-help">
<p id="password-help">Must be at least 8 characters with one number.</p>
Broken reference due to a typo
Here the aria-describedby value uses a different case than the element’s id:
<input type="text" id="email" aria-describedby="emailHelp">
<small id="emailhelp">We'll never share your email.</small>
The fix is to make the id values match exactly:
<input type="text" id="email" aria-describedby="email-help">
<small id="email-help">We'll never share your email.</small>
Multiple IDs with one missing
When listing multiple descriptions, every id must be present:
<!-- "format-hint" exists but "length-hint" does not — this triggers the error -->
<input type="text" id="username" aria-describedby="format-hint length-hint">
<span id="format-hint">Letters and numbers only.</span>
Fix it by adding the missing element:
<input type="text" id="username" aria-describedby="format-hint length-hint">
<span id="format-hint">Letters and numbers only.</span>
<span id="length-hint">Between 3 and 20 characters.</span>
Removing the attribute when no description is needed
If the descriptive content has been removed and is no longer relevant, simply remove the aria-describedby attribute:
<input type="text" id="search">
Ready to validate your sites?
Start your free trial today.