When a menu block is placed using Layout Builder, the rendering of the HTML ID attribute is missing a unique identifier, resulting in an "incomplete" -menu suffix. This is impactful because if two such menu blocks are placed on the same page, it will result in two identical HTML attributes, which is invalid markup and has accessibility implications (e.g., https://www.w3.org/WAI/WCAG21/Techniques/html/H93).
Example output:
<nav role="navigation" aria-labelledby="-menu" data-layout-content-preview-placeholder-label=""Main navigation" block" class="block block-menu navigation menu--main">
<h2 id="-menu">Main navigation</h2>
...
Steps to Reproduce
1. Enable Layout Builder on a node type.
2. Place a menu block into a node layout using the Layout Builder interface.
3. Inspect the menu block output. Note that the id is always -menu as is the aria-labelledby
Impact
If two instances of the menu block are placed, this will result in duplicate HTML ID attributes, resulting in inaccessible markup.
Proposed resolution
Update the SystemMenuBlock block plugin to supply a unique ID attribute by default. This ID will be used by existing templates to populate the ID attribute associated with the block title as well as the aria-labelledby attribute that points to that HTML ID.
Acceptance criteria
When a menu block is placed on a page using Layout Builder, it has markup that includes a valid, unique HTML ID attribute as well as an aria-labelledby attribute that matches that HTML ID value.
| Comment | File | Size | Author |
|---|---|---|---|
| #49 | Screenshot 2025-07-18 at 11.01.29 AM.png | 134.85 KB | themusician |
| #32 | inteerdiff_31-32.txt | 3.69 KB | _utsavsharma |
| #21 | interdiff_19-20.txt | 837 bytes | ramya balasubramanian |
| #9 | interdiff_2_8.txt | 2.14 KB | rishabhthakur |
Issue fork drupal-3073895
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
Comment #2
tim.plunkettHere's a patch. Not really helpful for anyone, as it uses a UUID. But at least the markup is valid.
Note that this is really a bug in block module for wrongly assuming there will be an ID available...
Comment #6
rishabhthakur commentedComment #7
rishabhthakur commentedReview Original patch https://www.drupal.org/files/issues/2019-08-12/3073895-id-2.patch it was written in wrong way to attached ID attributes with Block.
New Patch applied & tested with drupal 8.9.x , 9.0.x & 9.1.x versions
its working fine
Please review and confirm
Comment #9
rishabhthakur commentedUpload Interdiff for last patch with new patch
Comment #10
rishabhthakur commentedOn Code base its working but as Test cases fail so maybe we need to change on Test case
Correct me if I'm wrong.
Need Review on patch & test cases
Comment #11
rishabhthakur commentedComment #12
ramya balasubramanian commentedComment #13
ramya balasubramanian commentedFixing test cases file.
Comment #14
ramya balasubramanian commentedHi,
I have missed this function testContextAwareBlock in the previous patch. Updated the patch again.
Comment #17
ramya balasubramanian commentedComment #18
ramya balasubramanian commentedFixing the test cases on section render test file.
Comment #19
ramya balasubramanian commentedFixing code standards.
Comment #21
ramya balasubramanian commentedFixing the coding standard.
Comment #22
tim.plunkettNW for the missing test coverage.
Also #21 was the opposite, there should be spaces there
Comment #23
ramya balasubramanian commentedApologies for the late reply @tim.plunket was bit occupied. I guess the test cases were already present. I didn't get your comment about test cases. Can you please tell me which test cases you are talking about.
Comment #25
nwom commentedShould this perhaps be merged with #3020876: Contextual links of reusable content blocks are not displayed when rendering entities built via Layout Builder?
Comment #28
jay.dansand commentedSimple re-roll of the patch from #21 so it applies cleanly with composer-patches on 9.3.x and 9.4.x. No interdiff because no code lines changed, only surrounding context.
Comment #31
bradallenfisher commentedlatest patch doesn't apply to 9.5
Comment #32
_utsavsharma commentedPatch for 9.5.x.
Comment #33
_utsavsharma commentedComment #34
bradallenfisher commentedthe patch in #32 applies cleanly and the functionality has returned. thanks!
Comment #35
smustgrave commentedThis issue is being reviewed by the kind folks in Slack, #needs-review-queue-initiative. We are working to keep the size of Needs Review queue [2700+ issues] to around 400 (1 month or less), following Review a patch or merge request as a guide.
For the specific test coverage still needed.
Also I see that the tests were updated is that because this attribute is now required? Won't this break existing sites that don't have it?
Comment #39
danielvezaOpened an MR with a slightly different approach.
The existing patch will add an ID to every since block that is added via LB, which feels a bit overkill to solve this. Instead I've added a default ID that will be applied to system_menu_blocks if one does not exist. If people like this approach I'll add some tests
Comment #40
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue.
While you are making the above changes, we recommend that you convert this patch to a merge request. Merge requests are preferred over patches. Be sure to hide the old patch files as well. (Converting an issue to a merge request without other contributions to the issue will not receive credit.)
Comment #41
danielvezaComment #42
danielvezaComment #43
smustgrave commentedSeems a few other block--system-menu-block.html.twig in core. Is there a place we can add an assertion to?
Comment #45
mark_fullmerThis appears no longer to be an issue as of 11.3.x. Placing a menu block on the page successfully generates unique ID attributes; placing a second instance of the same menu block confirms that the ID attributes and corresponding aria-labelledby attributes are unique. I haven't tracked down the core commit responsible for this yet, but will set this to "Postponed" given the above.
Example markup of first instance of menu block:
Example markup of second instance of menu block:
Comment #47
mark_fullmerMy previous comment was wrong. This is still an issue in Drupal core, though it only manifests in the context of placing a menu block using Layout Builder.
Comment #48
mark_fullmerAgreed that modifying the Twig templates is not the best approach here. Instead, we can supply the ID attribute to all templates through the
SystemMenuBlock::build()method. The rendering logic for a unique ID can be modeled exactly onblock/src/Hook/BlockHooks::preprocessBlock(), per below:I've created an alternate merge request, at https://git.drupalcode.org/project/drupal/-/merge_requests/12664
Comment #49
themusician commentedThe latest patch appears to work. This is a screenshot of the code output when two unique menu blocks are inserted on the same page via layout builder.
Comment #50
smustgrave commentedNice! Glad we found a non template approach.
Moving to NW for the test coverage also issue summary may need some love to include proposed solution and maybe screenshots.
Thanks!
Comment #52
mark_fullmerComment #55
mark_fullmerComment #56
mark_fullmerComment #57
mark_fullmerComment #58
danielveza+1 on the approach from the latest MR. Looks like the newly added test coverage is failing to find the newly added ID though. I expect it will be a quick fix.
Comment #61
mark_fullmerUpdated to accommodate tests! Ready for further review.
Comment #62
danielvezaNice. Given it a (hopefully) last review. No issues, reckon this can be RTBC.
Comment #63
nod_I'm not sure the fix is appropriate.
IDs are not a backend concern, so it seems strange to handle that in the backend when the frontend can deal with it, we try not to rely on ids for styling and JS so the id could be random and it'd still work.
The MR https://git.drupalcode.org/project/drupal/-/merge_requests/6965 looks promising need to use
|clean_unique_idto make sure things don't break when you're dealing with ajax stuff.Comment #64
mark_fullmerIndeed, separate from styling and JS targets, HTML ID attributes, especially, as in this case, when associated with HTML heading tags, are used as in-page anchor links. Having a "permalink" through a consistent HTML ID, therefore, is preferable to a random one.
The approach in https://git.drupalcode.org/project/drupal/-/merge_requests/6965 would seem to provide a consistent ID, so if the Drupal core practice is to register HTML IDs in Twig templates, then MR 6965 would seem to be the right approach.
The disadvantage, for the sake of stating it explicitly, of updating (multiple) core Twig templates is that any sites that have defined their own
block--system-menu-blocktemplates will not receive this change. In my experience in Drupal development, the menu block template is frequently modified on a per site basis.Comment #67
daddison commentedI updated https://git.drupalcode.org/project/drupal/-/merge_requests/6965 to use
|clean_unique_idtwig filter in the block and updated test coverage.Comment #68
smustgrave commentedOriginal concern of updating the the twig templates directly is back. We will need a CR as contrib and custom themes won't have that change.
Comment #69
smustgrave commentedIf we are going to go the template route think we need a CR to least alert others that have overridden this template.
Comment #71
mh_nichtsHello,
I wanted to warn that https://git.drupalcode.org/project/drupal/-/merge_requests/6965 is acting only on the
heading_id(which is used to "label" the navigation viaaria-labelledby), but it's not acting on the blockiditself (attributes.id).It seems to me that https://git.drupalcode.org/project/drupal/-/merge_requests/12806 is acting at the right place, before Twig rendering, and really on the block
iditself : I agree with https://www.drupal.org/project/drupal/issues/3073895#comment-16254500 about the necessary separating of concerns.