Problem/Motivation

During the review of #2982853: Menu settings and path alias details should be closed even with values I got reminded to an oddity I've noticed in the aural interface a while ago in the context of the advanced sidebar on node edit forms. There the announcement of the expanded and collapsed state of detail elements was sort of off in voiceover - the announcement was even differing in between browsers (check aural_interface-safari.mp4 and aural_interface-firefox.mp4)

When checking the DOM I've finally realized what the actual problem is (check dom-safari.mp4 and dom-firefox.mp4). Turns out once the aria-expanded attribute is set to true it remains true no matter if the expansion state is changed another time. And the reason why the aural interface behaves different between safari and firefox, safari doesnt announce the same state a second time, while firefox announces the state change even though the state remains the same .

Steps to reproduce

  • Go to for example node/add/article
  • expand and collapse some of the detail elements in the advanced sidebar and observe the DOM and AOM while the detail element changes in between the expanded and collapsed state.

Proposed resolution

Make sure that the aria-expanded state is updated properly in a consistent manner.

P.s. one detail i am uncertain about, if you check the open attribute on the associated details element in safari you have open while in firefox you get open="". not sure if that would require additional action

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

Issue fork drupal-3571628

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

rkoller created an issue. See original summary.

debdeep.mukhopadhyay’s picture

I am working on this

debdeep.mukhopadhyay’s picture

Status: Active » Needs review
StatusFileSize
new8.27 MB

Hi @rkoller,

I tested this on Drupal 11 (DDEV) and applied a small fix in core/misc/details-aria.js.
Now aria-expanded correctly toggles when opening and closing the summary.

I’ve also attached a short screen recording showing it working on my local setup.
Thank you very much for reviewing when you have time.

rkoller’s picture

Status: Needs review » Needs work

thank you for picking up and working on the issue @innoraftDebdeep! I've checked out MR14647 and first just manually tested it. for me the problem persists in safari, firefox, and edge . I am not a developer, so i am not entirely sure, but in the diff only the ternary operator for the open attribute on the detail element was changed, the code for the aria-expanded on the summary element hasnt had a change.

on a side note, about my P.S in the proposed resolution section in the issue summary. I've noticed and reliazed while testing your MR, when i do a reload clearing the caches in firefox and safari (by clicking the reload button with pressed alt button) the attribute is open="open". After collapsing and expanding the detail element again it becomes the already described open in safari and open="" in firefox.

smustgrave’s picture

Issue tags: +Needs tests

@debdeep.mukhopadhyay thank you for taking an interest, looking at the MR the comment kind of concerned me as it appears it came from AI so wanted to share this https://www.drupal.org/docs/develop/issues/issue-procedures-and-etiquett....

debdeep.mukhopadhyay’s picture

Status: Needs work » Needs review
StatusFileSize
new616.7 KB

Hi @rkoller and @smustgrave,
thank you very much for taking the time to review and for the helpful feedback.

@rkoller —
Thank you for testing the MR and for the detailed notes.
I tested the patch again locally on Firefox, Safari and Edge, and for me the issue is resolved in all three browsers.
I have also added a screen recording to show the behavior after applying the patch.

One small note: after applying the patch, a full cache clear is required.
If the browser cache (and Drupal cache) is not cleared, the old behavior can still appear.
It is possible that the issue you are seeing is caused by cached assets.

Regarding the observation about open="open" after a hard reload and then changing after toggling — I was also able to reproduce that behavior before applying the patch, but after clearing caches and retesting with the patch, the behavior is consistent for me.

@smustgrave —
Thank you for pointing out the issue etiquette documentation. I really appreciate the reminder.

I would like to clarify that I am actively working on and testing this issue myself.
While I sometimes use tooling to help with wording, the investigation, implementation and testing of the fix were done by me, and I fully understand the responsibility of contributing accurate and reviewable work.

If you notice anything specific in the MR that should be improved (especially around tests, since the issue is now tagged with Needs tests), I would be very happy to address it.

Thanks again to both of you for the review and guidance.

rkoller’s picture

Status: Needs review » Needs work
StatusFileSize
new363.43 KB

i am still unable to confirm that the MR is working as expected on my end and i always clear caches after checking out a MR.

and looking at the video you provided the aria-expanded state != to the actual state in the ui. so also on your end the behavior is buggy:

a node edit form with the menu settings detail element expanded and the devtools underneath showing aria-expanded attribute as false

rkoller’s picture

i also copy over a few comments from the discussion https://drupal.slack.com/archives/C2ANFUGGG/p1770308283488189 on the drupal slack in the accessibility channel by @cwilcox808 potentially relevant for the solution:

Why does details-aria.js even exist? <summary>elements inherently have an aria-expanded state that's changed when its <details> is opened and closed.

If it's meant to correct times when there have been browser bugs (looking at you, Safari), perhaps there should be a comment in the file, justifying its existence.

If it should remain, iInstead of acting on a 'click' event on the <summary>, it should act on the toggle event on the <details>.

*the aria-pressed comment he is referring to in the following was in comment to my previous note in the discussion about the following comment in the details-aria.js file "Handles `aria-expanded` and `aria-pressed` attributes on details elements."

I saw a previous commit already removed aria-pressed from details-aria.js.

Expanding <details> to expose targeted fragments within is a nice idea for details.js to attempt. <summary> elements automatically have the .ariaExpanded = true property when their <details> element is in the open state so there shouldn't be any aria-expanded attributes on them at all.

BTW, opening <details> to expose targeted fragments is now native behavior, at least within browsers that support the new global attribute value, hidden="until-found" (note that Firefox and Safari apparently have bugs so they'll open them but not scroll them into view). It might go back a little further, to when browsers switched closed <details> contents from being display: none to content-visibility: hidden. or when they supported the ::details-content pseudo-element.

debdeep.mukhopadhyay’s picture

Hi @rkoller,
Thank you very much for your detailed review and for sharing the discussion from the accessibility channel.

Sorry for the confusion and for my earlier approach — I initially handled the state on the summary click event, which indeed reads the state before the element is actually toggled. That was my mistake.

Based on your feedback, I have updated the implementation to listen to the element’s toggle event instead, so the real open/closed state is reflected when updating aria-expanded. This should avoid the mismatch between the visual state and the accessibility state that you pointed out in the video.

Thank you again for your patience and for the very helpful guidance.

rkoller’s picture

StatusFileSize
new1.06 MB

thanks for updating the MR @debdeep.mukhopadhyay! i've tested in the latest versions of safari, safari technology preview, firefox, and edge in macos 26.2 and in each of them the state is announced correctly now, great! check the recording voiceover.mp4, was done in safari 26.2 with voiceover. from a manual testing perspective this looks good. from a technical perspective i am not qualified to assess if it is the right approach, i am not a developer. i defer to either @smustgrave or @cwilcox808 who i will ping in the drupal slack, they can better judge the approach than me. and after there is a consensus about the taken approach for the solution the MR still needs tests.

cwilcox808’s picture

I don't have the means to test the code but I can comment on what I read.

Reading the code, there are a number of improvements; using the 'toggle' event, using the details.open property, removal of jQuery dependency.

This:

details.querySelector('summary')

should be changed to this:

details.querySelector('> summary')

If the summary element isn't a direct descendant of the details element, it's not valid and won't be functional (it will effectively be a div).

I think this can be simplified:

summary.setAttribute(
  'aria-expanded',
  details.open ? 'true' : 'false',
);

To:

summary.setAttribute('aria-expanded', details.open);

The boolean value of .open is cast to the strings 'true' or 'false'.

Something I see in production is <summary role="button" aria-controls="edit-menu" aria-expanded="false" …> exists without JavaScript running. Because aria-expanded can only be altered by JavaScript, the attribute should not be present unless JavaScript is running. Otherwise, the details element will always be announced as "collapsed." I didn't realize this script was being used on a summary with role="button", effectively changing the details / summary into a generic "disclosure widget." If JavaScript doesn't run and the summary has role="button", it will break the details / summary, even without the presence of aria-expanded.

I can't tell which details / summary elements this script is meant to act on, for the moment I'm going to assume it's only used on ones like those in the page authoring sidebar (Menu settings, URL alias, etc.) that are turned into generic disclosure widgets. How it should work is the summary elements have no ARIA attributes (including role) so that if JavaScript doesn't run, the element's inherent role and state can still work. When the JavaScript runs, it adds the role="button" and the aria-expanded with the state that matches whether the details element is open or not. It should probably only add the role attribute if the element doesn't already have one. Don't add aria-controls, it's pointless in this case (and in most cases).

debdeep.mukhopadhyay’s picture

Hi @cwilcox808,
Thank you very much for the detailed feedback and for taking the time to review and test this.

Sorry for the earlier confusion and for my previous implementation — that was my mistake.

I have now updated the code based on the comments, so that:

the state is handled on the element’s toggle event,

only a direct child is targeted,

the real details.open state is used,

and existing widgets (summaries that already have a role) are respected, while still keeping aria-expanded in sync.

According to my latest testing, the visual state and the announced state now stay in sync.

Thank you again for your patience and for the very helpful guidance.

rkoller’s picture

StatusFileSize
new850.38 KB
new2.06 MB

thanks for updating the MR incorporating the suggestions by @cwilcox808... i've tested the changes manually with keyboard and mouse in combination with voiceover. in safari and chrome things look good. i was only able to get to an off state where the ui showed one state and the aural interface the opposite. but i am unable to reproduce that consistently neither have any idea how i've managed to trigger that. BUT in firefox i am able to to trigger it more consistently, i only dont know what pattern is behind it yet. "maybe" it depends on the speed the details element is clicked by mouse at first? as i said not entirely sure about the root cause the state gets off track. i did another test and included the devtools in the video in firefox. i did the same steps as the previous video but as you can see this time luckily only the comments details element is off url alias remains correct. luckily because the summary element for the comment details element is selected in the devtools and there even voiceover announces expanded button group for the comments the aria-expanded state is false. not sure how that is possible at all? i am using the latest firefox developer edition.

debdeep.mukhopadhyay’s picture

Hi @rkoller,
Thank you very much for the additional testing and for sharing the recordings.

From my side, I can also observe that in Firefox the visual state of the element and the announced state can occasionally become out of sync, while the behavior in Safari and Chrome appears to be consistent. This seems to be a rare interaction or timing-related edge case.

If you agree, I would like to propose adding a very small and limited fallback that simply re-synchronizes the aria-expanded state on interaction with the element itself, while still keeping the toggle event as the primary and authoritative source of truth.

I would greatly appreciate your guidance on whether this approach would be acceptable.

rkoller’s picture

@debdeep.mukhopadhyay i dont know if your suggested approach would be the right choice, as i've mentioned before i am not a developer and therefore unfortunately unable to provide any feedback about that kind of implementation details. :( i guess @cwilcox808 or @smustgrave are able to assess your suggestion in #15 better than me.

jojo_martin’s picture

I had just run into this issue and was happy to find that it's already logged! In my case, it came up for displays using the 'Details' field group and then I went looking at admin pages and noticed it in details elements there too.

I can confirm that the changes in the MR ensures the accuracy of the aria-expanded attribute within details elements, at least in firefox and chrome.

rkoller’s picture

thank you for testing @jojo_martin! out of curiosity after you wrote the MR is fixing the issue for you in firefox and chrome, are you unable to reproduce the problem outlined in #14?

jojo_martin’s picture

@rkoller I just checked with VoiceOver and firefox, and was able to reproduce the inconsistent issue described in #14, which seems to be more likely an issue with VoiceOver based on a similar issue I found with a quick search for VoiceOver not correctly announcing changes to the aria-expanded attribute: https://bugs.webkit.org/show_bug.cgi?id=284804

I also found that the VoiceOver issue seemed to happen more if I used my mouse to interact with the expandable element, and it was more reliable if I just used keyboard navigation. Either way, it doesn't seem like an issue on the drupal side of things from what I can tell. So, it likely doesn't need to be a blocker for this issue?

debdeep.mukhopadhyay’s picture

Hi @jojo_martin,@rkoller
Thank you very much for testing again and for sharing your findings.

If this appears to be a VoiceOver/browser-level issue rather than something caused by the Drupal implementation, that makes sense based on the current behavior we are seeing.

From my side, the visual state and the aria-expanded attribute remain in sync, and I’m not seeing evidence of a Drupal-side mismatch.

I’m happy to follow the maintainers’ guidance on whether this can move forward as-is or if any further adjustments are needed.

debdeep.mukhopadhyay’s picture

Hi @jojo_martin,@rkoller,

I hope you are doing well.

I would like to gently follow up on this issue. I had worked on this earlier and shared my contribution/merge request after spending considerable time on analysis, implementation, and testing to ensure the proposed solution behaves as expected.

Since there has been no update or feedback for quite some time, could you please take a moment to review the contribution when convenient? If any further changes or improvements are required, I would be very happy to address them.

If everything looks good, I would sincerely appreciate if the issue could be moved to the next appropriate status. I would also be grateful to be credited for my contribution.

Thank you very much for your time and support.

kentr’s picture

@debdeep.mukhopadhyay Thank you for working on this!

I understanding that the wait can be frustrating. Sometimes issues just take a while. We generally often use the priority and age of issues to determine next tasks [edit: and issue queue order].

I will note that the status is currently "Needs work" as of comment #8, which means that no one will be moving to review it.

The issue also has the tag "Needs tests", and the last pipeline failed. It can't be set to "Needs review" until those are resolved.

If you have added tests, please remove the "Needs tests" tag.

kentr’s picture

I also want to note that new tests should be FunctionalJavascript, and probably should use one of the existing routes in the form_test module that displays details elements.

mgifford’s picture

Issue tags: +wcag412

Just adding some identifiers:

Bug ID: DRU-9da94f27 (instance) / DRU-e1099604 (pattern)

Primary route: /node/add/article (details behaviors also exercised on /form-test/group-details)

WCAG SC: 4.1.2 (A)

Rule: aria-expanded-sync

kentr’s picture

Note: There's a discussion on whether or not we really do want to use a modified details / summary element in #3573865: Details preprocess incorrectly adds role to summary.

Why does details-aria.js even exist? <summary>elements inherently have an aria-expanded state that's changed when its <details> is opened and closed.

If it's meant to correct times when there have been browser bugs (looking at you, Safari), perhaps there should be a comment in the file, justifying its existence.

If it should remain, iInstead of acting on a 'click' event on the <summary>, it should act on the toggle event on the <details>.

details-aria.js was added in #2472177: Collapsible fieldset have duplicated and wrong aria-expanded. It looks like it was just to add dynamic behavior for aria-expanded in the transition to a native details.

But does the native expanded state work correctly in Safari now? Safari + VoiceOver doesn't announce the state change for me on a plain details / summary such as on this a11ysupport.io test page. This is Safari 26.5.2, MacOS 14.8.

Does effectively changing the details / summary into a generic "disclosure widget" by fixing aria-expanded and moving the aria-role to JavaScript per #12 satisfy this recommendation for "creating your own" disclosure widget by Scott O'Hara?:

If your goal is to create an absolutely consistent disclosure widget behavior across browsers, i.e., ensuring that all <summary>s are exposed as expand/collapse buttons, then you’d be better off creating your own using JavaScript and the necessary ARIA attributes. You’d lose out on the find-in-page functionality that the native disclosure widget provides… but depending on the type of disclosure widget you’re creating, you may actually want that.

I'm planning to rebase this MR and look into the failing test.

kentr’s picture

Rebased, and pipeline is passing.

Also, this code runs on all details elements as far as I can tell.

In Claro, the details on the node edit form are called “accordion“ style. I don’t know of a live example of a “standard” details element in core.

Maybe there’s one in the views UI or field UI somewhere. There is one in the form_test module at /form_test/details-contains-required-fields.

Some questions

  1. What are the advantages, if any, of using a modified details / summary like the current MR here does (versus using other HTML elements)?
  2. What's the advantage of using the toggle event instead of a click event?

    We have disclosure widget code elsewhere which we could centralize into a helper function so that it’s DRY and standardized. If we're effectively turning details / summary into a generic disclosure widget, could we use that code here instead of something new for details / summary?

cwilcox808’s picture

I don't have all the context but here are my thoughts about your questions:

  1. details / summary can still expand/collapse when JavaScript fails for some reason. If the elements are used in an interface that's useless without JavaScript working, this isn't an advantage.
  2. A 'click' event is only triggered when the summary is activated, the 'toggle' event should be triggered regardless of how the details element is expanded or collapsed. The details element contents now implicitly have the hidden=until-found property so they can expand if a user uses the browser's find in page feature to search for text that's within the element (this CSS prevents that: details:not([open])::details-content { display: none; }).

The only reason I'm aware of to add JavaScript is intermittent Safari+VoiceOver (macOS and iOS/iPadOS) bugs. Sometimes they're fine but there have been regressions more than once over the past 5+ years. Issues happen more often with macOS than Apple's mobile OSes. The typical issue is failing to announce when the state changes ("expanded," "collapsed").

kentr’s picture

Thanks @cwilcox808

Makes sense to me.

1. details / summary can still expand/collapse when JavaScript fails for some reason. If the elements are used in an interface that's useless without JavaScript working, this isn't an advantage.

Since we can't predict where it will be used, we probably need to plan for cases where this is a problem. I'm in the camp that there are many reasons this can happen that JavaScript can be unavailable. The Scott O'Hara article says that this also applies to "reader mode".

2. A 'click' event is only triggered when the summary is activated, the 'toggle' event should be triggered regardless of how the details element is expanded or collapsed. The details element contents now implicitly have the hidden=until-found property so they can expand if a user uses the browser's find in page feature to search for text that's within the element (this CSS prevents that: details:not([open])::details-content { display: none; }).

Sounds like we need to fix that, also.

The only reason I'm aware of to add JavaScript is intermittent Safari+VoiceOver (macOS and iOS/iPadOS) bugs. Sometimes they're fine but there have been regressions more than once over the past 5+ years. Issues happen more often with macOS than Apple's mobile OSes. The typical issue is failing to announce when the state changes ("expanded," "collapsed").

Safari may be in retrograde currently. @rkoller and I have both experienced that recently. It's helpful to know that it's happened more than once.

You made a good point about adding a comment regarding the purpose for the JavaScript.

kentr’s picture

Even a node edit form isn't useless without JavaScript. The rich text editor won't work, but standard fields will.

mgifford’s picture

This sounds quite like we're moving in the right direction.

Having a consistent pattern that helps us control inconsistent browser application would be good.

We can then bring this over to replace efforts here https://www.drupal.org/project/drupal/issues/3573865#comment-16692222

kentr’s picture

Closed #3573865: Details preprocess incorrectly adds role to summary as a duplicate and transferred credit. It's more like a subset than a duplicate...

Per #12, we should remove role="button" from the initial markup. This change can be taken from https://git.drupalcode.org/project/drupal/-/merge_requests/14777 for #3573865: Details preprocess incorrectly adds role to summary

I didn't realize this script was being used on a summary with role="button", effectively changing the details / summary into a generic "disclosure widget." If JavaScript doesn't run and the summary has role="button", it will break the details / summary, even without the presence of aria-expanded...

...How it should work is the summary elements have no ARIA attributes (including role) so that if JavaScript doesn't run, the element's inherent role and state can still work. When the JavaScript runs, it adds the role="button" and the aria-expanded with the state that matches whether the details element is open or not. It should probably only add the role attribute if the element doesn't already have one.

kentr’s picture

Actually, probably MR 16325 https://git.drupalcode.org/project/drupal/-/merge_requests/16325 is the right one for removing the role="button" from the initial markup.