Phenomenon
A conflict exists in Chromium’s Accessibility implementation where legacy ARIA attributes can override or mangle the state derived from the contenteditable attribute. Specifically, when a developer explicitly sets aria-readonly="false" (often done to be safe or when toggling states dynamically), Chrome sometimes internalizes this flag in a way that suggests a restricted state to the macOS and Windows Accessibility APIs. This leads screen readers like NVDA to announce the field as “read only” even though the user can type into it.
Reproduction Steps
- Create an element with
contenteditable="true"androle="textbox". - Add the attribute
aria-readonly="false". - Open Chrome (v122 - v124).
- Start a screen reader like NVDA or JAWS.
- Tab focus into the editor.
- Listen for the initial announcement.
Observed Behavior
- Screen Reader Voice: NVDA says “Type here, Edit, Read only.”
- User Confusion: The user is told the field is read-only, so they may stop trying to input text, despite the caret being active and blinking.
- Accessibility Tree: The
readonlybit in the platform-specific accessibility node (e.g., IA2 on Windows) is set to true incorrectly.
Expected Behavior
The contenteditable="true" state should define the primary editing capability. aria-readonly="false" should be redundant or ignored, and aria-readonly="true" should only take effect if the contenteditable attribute is also set to false.
Impact
- Non-Standard Workflow: Users who rely on screen readers are misled about the interactability of the application.
- Dynamic State Failure: Applications that toggle between “Read Mode” and “Edit Mode” via ARIA attributes experience “ghost” read-only states that persist after the user enters edit mode.
Browser Comparison
- Chrome 124: Confirmed conflict in certain Shadow DOM and Custom Element scenarios.
- Firefox: Correctly prioritizes the editable state; ignores
aria-readonly="false". - Safari: Generally consistent with ARIA mappings but has other
role="textbox"challenges.
References & Solutions
Mitigation: Avoid aria-readonly on Editable regions
Unless you are explicitly in a non-editable state, do not include the aria-readonly attribute at all. Use the presence or absence of the contenteditable attribute to signal the state to the browser.
function setEditMode(isEditable) {
const editor = document.getElementById('editor');
editor.contentEditable = isEditable ? 'true' : 'false';
// REMOVE aria-readonly instead of setting to false
if (isEditable) {
editor.removeAttribute('aria-readonly');
} else {
editor.setAttribute('aria-readonly', 'true');
}
}