Problem/Motivation

The state:readonly handler in core/misc/states.js sets the readonly attribute on <input> and <textarea> elements, but VoiceOver (and some other screen readers) does not announce readonly fields any differently from editable fields. This causes an accessibility barrier: users perceive the field as editable, but their input has no effect.

The readonly attribute exists as a distinct concept from disabled because:
- disabled fields do not submit their values with the form.
- disabled fields are skipped in the tab order.
- readonly fields do submit values and are focusable — they just prevent editing.

However, without an ARIA announcement, there is no perceivable difference for screen reader users between a readonly field and an editable one.

Steps to reproduce

1. Create a form with a text field that uses #states to become readonly based on another field's value.
2. Trigger the readonly state.
3. Focus the readonly field with VoiceOver enabled.
4. VoiceOver announces the field as a normal editable text field — there is no indication it is readonly.

Proposed resolution

Add aria-disabled="true" alongside the readonly property in the state:readonly handler. This causes screen readers to announce the element as "dimmed" (VoiceOver) or "unavailable" (NVDA/JAWS), making the readonly state perceivable.

When the readonly state is removed, the aria-disabled attribute should be removed entirely (not set to "false") to avoid leaving stale ARIA attributes in the DOM.

Issue fork drupal-3582687

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

sasanikolic created an issue. See original summary.

sasanikolic’s picture

Status: Active » Needs review

sasanikolic changed the visibility of the branch 3582687-readonly-state-is to hidden.

sasanikolic changed the visibility of the branch 3582687-readonly-state-is to active.

cilefen’s picture

Issue tags: +Accessibility
rkoller’s picture

I've manually checked with the form_test module, based on the steps outlined in the issue summary in #2921627: Do not use a CSS-only required marker in forms per WCAG 2.0 within the "javascript states API" section (thanks to @kentr for the pointer). I agree that the current state in Core has several problems. For one, the fields arent programmatically labeled as readonly (i am only testing in VoiceOver), but at the same time looking at the form_test module sighted users are also unable to clearly distinguish a regular field from a readonly field - you have either to read the field label or you have to notice after clicking within the field that there is no cursor available, an additional visual cue for distinguishing fields in those different states is missing completely.

Programmatically the MR is adding a differentiator by using the aria-disabled="true" attribute, the only problem, semantically a disabled element is different from a readonly element (https://stackoverflow.com/a/7730719). That way, using aria-disabled is sort of misleading and semantical the wrong pick. The appropriate pick would be aria-readonly but looking at https://adrianroselli.com/2024/11/avoid-read-only-controls.html and https://adrianroselli.com/2022/11/brief-note-on-aria-readonly-support-ht... citing Adrian Rosellis final verdict

In short, if you think read-only controls are a hassle to style and script and properly expose, imagine how much of a hassle they are to use. Probably avoid them.

it doesn't sound like applying the readonly state is a desirable approach. So I wonder if the usage of the readonly state should be revisited in general - how things are announced in the aural interface and how things are communicate and visually presented in the ui for sighted users? @kentr pointed to a comment in one of the two articles where adrian roselli suggest some viable approach https://adrianroselli.com/2024/11/avoid-read-only-controls.html#comment-... . On a related note I guess how to handle the readonly state should probably be revisited alongside with the related disabled state. that disabled state contains also a few problems.

kentr’s picture

Summarizing so far:

  • disabled and readonly are distinct states with different purposes.
  • @rkoller notes that they are semantically distinct concepts and shouldn't be combined as a general rule.
  • In the articles referenced by @rkoller, Adrian Roselli notes problems for screen reader users and all users, and he recommends not using readonly controls to begin with.

Additionally:

With VoiceOver on the MDN basic readonly example, it's also not announcing that the field is read-only as I focus the fields.

VoiceOver does say "selectable". This is consistent with what I experience on a field whose readonly state is set with #states.

I'd say that this should be Closed (works as designed).

It might be helpful as a followup to put a documentation note somewhere that readonly controls are generally problematic and discouraged. One place I can think of is in the Accessibility Coding Standards.

smustgrave’s picture

Status: Needs review » Postponed (maintainer needs more info)

@sasanikolic as the reporter does that feedback make sense?

sasanikolic’s picture

Status: Postponed (maintainer needs more info) » Closed (works as designed)

@smustgrave Yes, I think the discussion here was fruitful with the conclusion that the readonly state should be revisited, together with the related disabled state. Mixing aria-disabled together with readonly is not a viable approach as I initially thought.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.