Problem

Messages currently have a role="contentinfo" wrapper for status messages (compared with role="alert" for error messages).

contentinfo is inappropriate here, because it's supposed to be for information about the document as a whole, not status updates. It's a landmark role, and there should only be one such contentinfo region per page. It's intended to serve the same purpose as a top-level HTML footer element. Using contentinfo for messages may be present some confusion for users who navigate by landmark regions.

Proposed resolution

  1. Replace the role="contentinfo" from the message-type wrapper DIV with role="region" and change the aria-label to "System Messages".
  2. Leave the <div role="alert"> in place for the error message group.
  3. Add a <div role="status"> wrapper for Status and Warning message groups.

Here's a simplified before/after summary of this.

Structure BEFORE:

div role="contentinfo" aria-label="Status message"
    h2.visually-hidden
    ul

div role="contentinfo" aria-label="Error message"
    div role="alert"
        h2.visually-hidden
        ul

Structure AFTER:

div role="region" aria-label="System messages"
    
    div role="status" // Status & Warning messages
        h2.visually-hidden
        ul

    div role="alert"  // Error messages
        h2.visually-hidden
        ul
CommentFileSizeAuthor
#78 2942404-contentinfo-landmark.patch10.39 KBbapplejax
#77 Screenshot 2024-08-30 at 14.50.00.png378.75 KB2dareis2do
#74 Screenshot 2024-08-29 at 00.26.28.png523.73 KB2dareis2do
#74 Screenshot 2024-08-29 at 00.26.41.png461.82 KB2dareis2do
#74 Screenshot 2024-08-29 at 00.51.02.png392.27 KB2dareis2do
#74 Screenshot 2024-08-29 at 00.50.45.png339.72 KB2dareis2do
#74 Screenshot 2024-08-29 at 00.50.32.png384.42 KB2dareis2do
#74 Screenshot 2024-08-29 at 00.48.09.png440.52 KB2dareis2do
#74 Screenshot 2024-08-29 at 00.45.46.png293.02 KB2dareis2do
#73 Screenshot 2024-08-29 at 00.30.03.png189.3 KB2dareis2do
#73 Screenshot 2024-08-29 at 00.30.58.png182.93 KB2dareis2do
#72 Screenshot 2024-08-28 at 21.45.48.png408.84 KB2dareis2do
#71 Screenshot 2024-08-28 at 21.44.21.png423.57 KB2dareis2do
#70 Screenshot 2024-08-28 at 21.41.46.png415.23 KB2dareis2do
#70 Screenshot 2024-08-28 at 21.37.20.png456.45 KB2dareis2do
#68 Screenshot 2024-08-28 at 00.37.18.png111 KB2dareis2do
#65 2942404-65-11.x.patch11.71 KBbapplejax
#58 2942404-56-11.x.patch11.66 KBprashant.c
#55 reroll_diff_49-55.txt688 byteshardik_patel_12
#55 2942404-55.patch9.62 KBhardik_patel_12
#53 reroll_diff_49-53.txt8.98 KBhardik_patel_12
#53 2942404-53.patch15.45 KBhardik_patel_12
#49 2942404-49.patch9.62 KBedmoreta
#48 2942404-48.patch9.62 KBedmoreta
#46 2942404-messages-should-have-role-status.patch11.47 KBedmund.dunn
#43 interdiff_42_43.txt1.73 KBameymudras
#43 2942404-43.patch9.55 KBameymudras
#42 2942404-42.patch9.62 KBshubham chandra
#39 2942404-39.patch16.96 KBMunavijayalakshmi
#34 2942404-34.patch16.84 KBelusivemind
#29 interdiff_2942404_27-29.txt1.24 KBankithashetty
#29 2942404-29.patch16.14 KBankithashetty
#27 2942404_23-26.txt964 bytesandy-blum
#27 2942404_26.patch16.86 KBandy-blum
#23 interdiff_15_23.txt2.26 KBspokje
#23 status-messages-2942404-23.patch15.19 KBspokje
#15 status-messages-2942404-15.patch14.21 KBsd9121
#12 status-messages-role-2942404-12.patch10.71 KBlus
#11 status-messages-role-2942404-11.patch6.74 KBlus
#4 2942404-messages-bartik-headings-map-front-page.png19.29 KBandrewmacpherson
#4 2942404-devel-messages.png16.37 KBandrewmacpherson

Issue fork drupal-2942404

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

andrewmacpherson created an issue. See original summary.

andrewmacpherson’s picture

Note this issue might turn out to be a duplicate. I have a strong recollection that this has been discussed before, but I can't find an issue. I think it might be buried in a comment thread somewhere.

Not sure if we should require manual testing to demonstrate an actual improvement before committing this. Not all screen reader + browser combinations announce alert and status regions on page load, and I don't think the ARIA recs actually require them to do so. But status is the correct role semantically, so this fix is worth doing.

I've read about approaches which try to force alert and status announcements on page load by using JS to tweak the element just after page load. I'm against trying to manage that ourselves, let's leave it up to conforming browsers and AT to decide whether to announce them. This may have an added benefit of respecting user preferences for any AT that provides them.

mgifford’s picture

Makes sense to me:

https://w3c.github.io/html-aria/#index-aria-status
A container whose content is advisory information for the user but is not important enough to justify an alert, often but not necessarily presented as a status bar.

https://w3c.github.io/html-aria/#index-aria-contentinfo
A large perceivable region that contains information about the parent document.

andrewmacpherson’s picture

Here are some notes about the current markup. My proposal will be in a later comment...

A PHP snippet to generate some messages. I've been using this with Devel module's Execute PHP tool.

$messenger = \Drupal::service('messenger');

// $messenger->addMessage('Hello world', 'custom');

$messenger->addWarning('Dog goes "woof"');
$messenger->addStatus('cat goes "meow"');
$messenger->addError('mouse goes "squeek"');

$messenger->addWarning('frog goes "croak"');
$messenger->addStatus('elephant goes "toot"');
$messenger->addError('the seal goes "ow ow ow"');

$messenger->addWarning('Ducks say "quack"');
$messenger->addStatus('the fish go "blub"');
$messenger->addError('What does the fox say?');

Here's a screenshot of the messages you get from that PHP snippet:

3 warning messages (amber), followed by 3 status messages (green), followed by 3 warning messages (red).

A summary of the HTML markup looks like this (output from status-messages.html.twig, I've omitted classes from the Classy theme):

<div role="contentinfo" aria-label="Warning message">
    <h2 class="visually-hidden">Warning message</h2>
     <ul>
        <li></li>
        //...
    </ul>
</div>

<div role="contentinfo" aria-label="Status message">
    <h2 class="visually-hidden">Status message</h2>
    <ul>
        <li></li>
        // ...
    </ul>
</div>

<div role="contentinfo" aria-label="Error message">
    <!-- NOTE: error message type gets an extra wrapper div -->
    <div role="alert">
        <h2 class="visually-hidden">Error message</h2>
        <ul>
            <li></li>
            // ...
        </ul>
    </div>
</div>

Notes:

  • Messages are grouped by type, as a list (one UL per type).
  • Each message type gets a visually-hidden H2 (maximum one heading per type).
  • All 3 message types get a role="contentinfo" wrapper, with an aria-label containing the message type.
  • Warning messages get a role="alert" wrapper", as well as the role="contentinfo" wrapper.
  • The messages aren't guaranteed to be displayed in the order they were generated; the grouping by-type takes priority.

The headings structure for the front page (in Bartik) looks like this. It shows one H2 per message type.

Headings map when three message groups are present.

andrewmacpherson’s picture

Proposal - update status-messages.html.twig,/code> in system module and themes.

The role="contentinfo" is wrong. This role is for a landmark region, equivalent to HTML <footer>, with the extra proviso that there can only be one contentinfo landmark per document, and it must be a top-level landmark. I'm not sure how we ended up with role="contentinfo", but perhaps the idea was to make the messages easy to find using landmark regions. I like that idea, and we can use <div role="region"> instead, and it will count as a landmark region, as long as it has an ARIA label.

Proposed changes:

  1. Remove the role="contentinfo" from the message-type wrapper DIV.
    • Optionally, replace this with role="region", so it remains in landmark-region navigation for assisitive tech.
    • Otherwise, remove the aria-label, which won't be needed if it doesn't have a role.
  2. Leave the <div role="alert"> in place for the error message group.
  3. Add a <div role="status"> wrapper for Status and Warning message groups.

Here's a before/after summary of this. I've left out classes from Classy.

Structure BEFORE:

div role="contentinfo" aria-label="Status message"
    h2.visually-hidden
    ul

div role="contentinfo" aria-label="Error message"
    div role="alert"
        h2.visually-hidden
        ul

Structure AFTER:

div role="region" aria-label="Status message"   // EXISTING DIV
    div role="status"  // NEW DIV for status and warning message groups
      h2.visually-hidden
      ul

div role="region" aria-label="Error message"   // EXISTING DIV
    div role="alert"  // EXISTING DIV
        h2.visually-hidden
        ul

The only thing we are changing here is the value of some existing role attributes, and inserting an extra DIV wrapper for some groups (which is an implicit aria-live region, but otherwise not referred to by any existing CSS or JS). No classes or IDs are affected.

I think we could get away with this, and manage to avoid breaking any custom themes.

I don't think I've ever seen custom message templates on a web site's front-theme, though I expect some admin themes have done so.

Some other ideas:

  • Do we want separate role="region" for each message type, or a single role="region" for all messages, with 3 headings inside?
  • Drupal has 3 message levels (error, warning, status) but ARIA has two levels of message (alert, status). I'm not sure whether we should map Drupal's "warning" level to to and ARIA alert or status role. The existing markup suggests warnings use the status role, but my instinct is to give them the alert role. It doesn't have any effect on server-side messages, as assistive tech doesn't have to announce live regions after loading new pages. But it will could make a difference to JS messages from #77245: Provide a common API for displaying JavaScript messages. We don't have to decide this here, so long a we get it consistent between server and JS messages eventually.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

rainbreaw’s picture

I just discovered this issue while looking at Claro — https://www.drupal.org/project/drupal/issues/3079738#comment-13249979 — which also uses contentinfo for status messages. It looks like this has been open for some time. Is there a reason that I'm not aware of that we are still using contentinfo when we should be using status?

andrewmacpherson’s picture

Because no-one has worked on the issue, that's all :-(

damienmckenna’s picture

Version: 8.6.x-dev » 8.8.x-dev

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

lus’s picture

StatusFileSize
new6.74 KB

I have updated the status message templates for the basic and test themes by changing the role of contentinfo to region and adding a wrapper with role status around the warning and status messages.

# The tests are failing because of the role change I gues.

lus’s picture

StatusFileSize
new10.71 KB

Well I have tried to modify the tests cases, but it seems that there are still two erros, that I don't know how to solve.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

sd9121’s picture

Assigned: Unassigned » sd9121
sd9121’s picture

Assigned: sd9121 » Unassigned
Status: Active » Needs review
StatusFileSize
new14.21 KB

Please review the patch.

Thanks!

Status: Needs review » Needs work

The last submitted patch, 15: status-messages-2942404-15.patch, failed testing. View results

sd9121’s picture

Assigned: Unassigned » sd9121
andrewmacpherson’s picture

Assigned: sd9121 » Unassigned
Issue tags: +Global2020, +Bug Smash Initiative

@sd9121 - what has changed in your version of the patch, compared with the previous one?

This is a good issue to review during the Accessibility Bug Bash during DrupalCon Global 2020. Might need an issue summary update, following #5, to keep the proposed resolution up to date.

The later patches here don't have interdiffs, so it's worth checking what changed from one version to the next.

mgifford’s picture

With aria-label="Status message" & <h2 class="visually-hidden">Status message</h2> being used, won't the "Status message" message be said twice to AT that supports ARIA?

EDIT: I realize this is repeating what we did previously for D8.

EDIT2: More examples to look at here
https://www.davidmacd.com/test/using-wai-aria-in-html.html#rec-legend
https://www.smashingmagazine.com/2015/04/its-alive-apps-that-feed-back-a...
https://developer.paciellogroup.com/blog/2014/03/screen-reader-support-a...

Also if we use role="status" we might also need to add aria-live="polite".

I'm in the midst of comparing a great many examples of government status messages at the moment. There isn't a great general consensus on this.

sd9121’s picture

@andrewmacpherson,

I saw the previous patch got failed. I have rerolled the patch.

Thanks!

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

bnjmnm’s picture

A duplicate of this issue was created a few months ago. I closed that one as there as there's more context here, but it may be worth consulting the most recent patch to see how the test failures in #15 can be avoided: #3159933: Status Message in Claro on theme install should have role="status" or similar instead of contentinfo

spokje’s picture

StatusFileSize
new15.19 KB
new2.26 KB

Reroll and test failures fix of patch #15

Thanks @bnjmnm for pointing out the duplicate where I've shamelessly copied carefully crafted the fixes from...

spokje’s picture

Added Needs issue summary update-tag to address #18

Might need an issue summary update, following #5, to keep the proposed resolution up to date.

Because of that, keeping this at Needs work although we have a working and green patch.

Note: I think we don't need extra tests for this since we altered 4 tests covering this already.
Then again, I've been very wrong about tests before...

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

andy-blum made their first commit to this issue’s fork.

andy-blum’s picture

Status: Needs work » Needs review
StatusFileSize
new16.86 KB
new964 bytes

New patch is a re-roll of #23 for 9.3.x and adds the fix to Olivero's status-messages.html.twig file

spokje’s picture

Status: Needs review » Needs work
ankithashetty’s picture

StatusFileSize
new16.14 KB
new1.24 KB

Updated patch in #27 to fix custom command failures. Retaining status as "Needs work" for issue summary update as mentioned in #24 , thanks!

andy-blum’s picture

Issue summary: View changes

Updating issue summary's proposed resolution per #5, with a couple of alterations:

  1. I like the idea of keeping messages as a landmark role for discoverability, but I don't think each message type warrants its own landmark region. I've updated the proposal in comment #5 to bundle all messages within a role="region" with aria-label="Status messages".
  2. In my opinion, warnings and errors belong bundled within the role=alert, but without a clear rule or set of examples, I'm deciding to maintain the pattern we currently have where only error messages are given the alert role. mgifford suggested we add aria-live="polite" in #19, but I don't think that's necessary as the drupal.message API uses drupal.announce to announce new JS-based messages to users.
mgifford’s picture

Issue tags: +wcag131

Tagging this as a WCAG 1.3.1 issue.

rosk0’s picture

Crosslinking, there is definitely an effort duplication between this issue and linked.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

elusivemind’s picture

StatusFileSize
new16.84 KB

Re-rolled for the latest 9.3.9 (9.3.x)

chi’s picture

+          <div class="messages__content">
+            {% if messages|length > 1 %}
+              <ul class="messages__list">
+                {% for message in messages %}
+                  <li class="messages__item">{{ message }}</li>
+                {% endfor %}
+              </ul>
+            {% else %}
+              {{ messages|first }}
+            {% endif %}

Is it a good idea to use UL and LI with role="generic" when only one message needs to be printed? That would simplify styling as the markup is not changing.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

jayhuskins’s picture

I was unable to apply patch #34. It was rejected in the Olivero theme.

Munavijayalakshmi’s picture

Assigned: Unassigned » Munavijayalakshmi
Munavijayalakshmi’s picture

StatusFileSize
new16.96 KB

Re-rolled patch for 9.5.x.

Munavijayalakshmi’s picture

Assigned: Munavijayalakshmi » Unassigned

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

shubham chandra’s picture

StatusFileSize
new9.62 KB

Re-rolled pacth #29 in Drupal 10.1.x

ameymudras’s picture

Status: Needs work » Needs review
StatusFileSize
new9.55 KB
new1.73 KB

Trying to fix CCF issue with patch #42

Status: Needs review » Needs work

The last submitted patch, 43: 2942404-43.patch, failed testing. View results

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

edmund.dunn’s picture

Rerolled for 10.1. Applies cleanly.

edmoreta’s picture

#46 it's not working

edmoreta’s picture

StatusFileSize
new9.62 KB

Tests might fail and they will require extra work

edmoreta’s picture

StatusFileSize
new9.62 KB
marvil07’s picture

Status: Needs work » Needs review

Back to NR for the new patches.

The last submitted patch, 48: 2942404-48.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 49: 2942404-49.patch, failed testing. View results

hardik_patel_12’s picture

Status: Needs work » Needs review
StatusFileSize
new15.45 KB
new8.98 KB

Solving failed test cases and rerolling for 11.x

smustgrave’s picture

Status: Needs review » Needs work

Caused massive test failures.

Was previously tagged for issue summary, has that been completed.

hardik_patel_12’s picture

StatusFileSize
new9.62 KB
new688 bytes

Rerolling to 11.x from #49.

Prashant.c made their first commit to this issue’s fork.

prashant.c’s picture

Status: Needs work » Needs review
StatusFileSize
new11.66 KB

Submitting Patch for 11.x and raising the MR as well.

Thank you.

smustgrave’s picture

Status: Needs review » Needs work

Large number of failures.

Also was previously tagged for issue summary update.

@Prashant.c there's no interdiff added what did you do different from #55

bnjmnm’s picture

I opened MR 6112 as patches don't seem to be testing properly. Most of the 1800+ test failures in #55 and #58 do not seem to have anything to do with the code changes. Among other things there are "missing composer.lock" messages, and that file is not touched in either patch. The MR I created is failing ~3 tests, so those will need to be addressed, but that should be straightforward compared to the unexpectedly huge fail count that suddenly appeared (and not at all the fault of @Prashant.c or @Hardik_Patel_12)

hardik_patel_12’s picture

@bnjmnm, I have observed similar challenges in several other issues. When using the patch method, it leads to significant unrelated failures for the composer and other components, similar to the problems described in issues #55 and #58. However, the Merge Request (MR) method is functioning properly.

neclimdul made their first commit to this issue’s fork.

neclimdul’s picture

bapplejax’s picture

StatusFileSize
new11.71 KB

Submitting a re-roll of patch #58 to allow for a clean application to 10.3.x. Some syntax and line number changes.

bapplejax’s picture

the 65-11.x.patch is a reroll to cleanly apply patch #58 to 10.3.x

2dareis2do’s picture

The role="contentinfo" is wrong. This role is for a landmark region, equivalent to HTML , with the extra proviso that there can only be one contentinfo landmark per document, and it must be a top-level landmark. I'm not sure how we ended up with role="contentinfo", but perhaps the idea was to make the messages easy to find using landmark regions. I like that idea, and we can use <div role="region"> instead, and it will count as a landmark region, as long as it has an ARIA label.

Agreed that there should not be more than one contentinfo per page.

Within any document or application, the author SHOULD mark no more than one element with the contentinfo role.

https://www.w3.org/TR/wai-aria-1.2/#contentinfo

How about just using complementary role. Status, Error or Notice is often (not always) related to the page you are on.

https://www.w3.org/TR/wai-aria-1.2/#complementary

The complementary role indicates that contained content is relevant to the main content. If the complementary content is completely separable from the main content, it may be appropriate to use a more general role.

Complimentary also supports header and footer, which might be useful.

2dareis2do’s picture

StatusFileSize
new111 KB

Looking at output from Claro, I think that they have it about right. i.e. one message per notice.

Markup is output in a single region.

<div class="region region-highlighted">
<div class="messages-list" data-drupal-messages="">
<div class="messages__wrapper">
<div class="messages messages--status messages-list__item" role="status" aria-labelledby="status-104584368505794-title" data-drupal-message-id="status-104584368505794" data-drupal-message-type="status">
    <div class="messages__header">
      <h2 id="status-104584368505794-title" class="messages__title">
        Status message
      </h2>
    </div>
    <div class="messages__content">
      Antibot (system_clear_cache): disabled
    </div>
  </div><div class="messages messages--status messages-list__item" role="status" aria-labelledby="status-263254492006824-title" data-drupal-message-id="status-263254492006824" data-drupal-message-type="status">
    <div class="messages__header">
      <h2 id="status-263254492006824-title" class="messages__title">
        Status message
      </h2>
    </div>
    <div class="messages__content">
      Caches cleared.
    </div>
  </div><div class="messages messages--status messages-list__item" role="status" aria-labelledby="status-288152668342968-title" data-drupal-message-id="status-288152668342968" data-drupal-message-type="status">
    <div class="messages__header">
      <h2 id="status-288152668342968-title" class="messages__title">
        Status message
      </h2>
    </div>
    <div class="messages__content">
      Antibot (system_performance_settings): disabled
    </div>
  </div>
</div>
</div>
</div>

In theory I see no issue in giving messages__wrapper or messages-listthe role contentinfo

2dareis2do’s picture

One challenge here is css frameworks like bootstrap use a single role for all types of notifications. i.e. role=alert

here is an example for warning.

<div class="alert alert-warning alert-dismissible fade show" role="alert">
  <strong>Holy guacamole!</strong> You should check in on some of those fields below.
  <button type="button" class="close" data-dismiss="alert" aria-label="Close">
    <span aria-hidden="true">&times;</span>
  </button>
</div>

To colour can also be changed. Here are some examples.

<div class="alert alert-primary" role="alert">
  A simple primary alert with <a href="#" class="alert-link">an example link</a>. Give it a click if you like.
</div>
<div class="alert alert-secondary" role="alert">
  A simple secondary alert with <a href="#" class="alert-link">an example link</a>. Give it a click if you like.
</div>
<div class="alert alert-success" role="alert">
  A simple success alert with <a href="#" class="alert-link">an example link</a>. Give it a click if you like.
</div>
<div class="alert alert-danger" role="alert">
  A simple danger alert with <a href="#" class="alert-link">an example link</a>. Give it a click if you like.
</div>
<div class="alert alert-warning" role="alert">
  A simple warning alert with <a href="#" class="alert-link">an example link</a>. Give it a click if you like.
</div>
<div class="alert alert-info" role="alert">
  A simple info alert with <a href="#" class="alert-link">an example link</a>. Give it a click if you like.
</div>
<div class="alert alert-light" role="alert">
  A simple light alert with <a href="#" class="alert-link">an example link</a>. Give it a click if you like.
</div>
<div class="alert alert-dark" role="alert">
  A simple dark alert with <a href="#" class="alert-link">an example link</a>. Give it a click if you like.
</div>

Certainly in Drupal, I would like to see the alerts beyond the standard status, warning and error. This would be useful for theming.

e.g. https://www.drupal.org/project/drupal/issues/3437924

2dareis2do’s picture

Updated example of Olivero.

So it appears that notifications are grouped together for Olivero theme. This is different from Claro that has separate notifications.

2dareis2do’s picture

StatusFileSize
new423.57 KB
2dareis2do’s picture

StatusFileSize
new408.84 KB
2dareis2do’s picture

Ok so it turns out message are displayed differently depending if big pipe is enabled.

If Big pipe is enabled messages are output in a group separately

If Big Pipe is disabled, messages are output separately as a group

2dareis2do’s picture

With Big Pipe enabled if there is a single message (claro) outputs role=contentinfo

With Big Pipe enabled
If message is single than one output is role=contentinfo
If output is more than one, output is role=status

I think error messages may be treated slightly differently.

2dareis2do’s picture

2dareis2do’s picture

2dareis2do’s picture

StatusFileSize
new378.75 KB

Just wanted to share how out looks with big pipe enabled in Drupal 10.2.7 (it's quite rare to see all types on a single page - see attached)

<div class="messages-list__wrapper">
                      
      <div role="contentinfo" aria-labelledby="message-error-title" class="messages-list__item messages messages--error">
                  <div role="alert">
                              <div class="messages__header">
                              <h2 id="message-error-title" class="messages__title">
                  Error message
                </h2>
                          </div>
                    <div class="messages__content">
                          One or more problems were detected with your Drupal installation. Check the <a href="/admin/reports/status">status report</a> for more information.
                      </div>
                  </div>
              </div>
                                  
      <div role="contentinfo" aria-labelledby="message-status-title" class="messages-list__item messages messages--status">
                              <div class="messages__header">
                              <h2 id="message-status-title" class="messages__title">
                  Status message
                </h2>
                          </div>
                    <div class="messages__content">
                          Antibot (system_themes_admin_form): disabled
                      </div>
              </div>
                                  
      <div role="contentinfo" aria-labelledby="message-warning-title" class="messages-list__item messages messages--warning">
                              <div class="messages__header">
                              <h2 id="message-warning-title" class="messages__title">
                  Warning message
                </h2>
                          </div>
                    <div class="messages__content">
                          No update information available. <a href="/admin/reports/status/run-cron?destination=/admin/appearance&amp;token=QJoWTyS5Bcf8KP_gvhmjUwm-s1rlrvSqL3XHgVsxEKQ">Run cron</a> or <a href="/admin/reports/updates/check?destination=/admin/appearance&amp;token=CBZRlulFTH8gW_6DOzzFPvANhv3hgZZGZgPlEhP9noY">check manually</a>.
                      </div>
              </div>
                  </div>

AFAICT messages are also grouped by type even with big pipe disabled.

Notice how there are multiple uses of role="contentinfo" on a single page which is obviously wrong.

In 10.3 I believe behaviour has changed slightly depending on whether big pipe is enable or not.

bapplejax’s picture

Rerolling patch #65 for Drupal 10.3.8. Line number updates and removal of edits to a template file where changes were addressed.

kthull’s picture

In addition to the role, we should also change the `h2` element to a `div` element. Looking at Olivero on a stock Drupal install, the error message renders before the `h1` thereby breaking the heading order.

mgifford’s picture

Is it better to loose the semantics by dropping the H2 or to retain the heading order? I'm not sure.

cwilcox808’s picture

My inclination is to keep the h2s.

  • Having h2s prior to a page's h1 is not a big deal. In the case of messages, they're not really sub-sections of the page's topic anyway, right?
  • If the messages are contained within a named landmark (e.g. div role="region" aria-label="System messages", there's already semantics present that are useful for navigation. However, surveys show headings are used far more than landmarks by screen reader users for navigation.
itmaybejj’s picture

I'd vote to keep the H2. Sub-themes might place this block in other regions.

kthull’s picture

Having h2s prior to a page's h1 is not a big deal. In the case of messages, they're not really sub-sections of the page's topic anyway, right

Failing WCAG checks is the current issue I'm facing, so from that point of view, it's problematic. An element with role=status already gets announced as if it were aria-live=polite which makes the h2 overkill.

From Google search summary:

When a heading is not necessary
If the role="status" area provides a short, simple update that doesn't require further breakdown or organization (like a progress bar), a heading might be redundant or even disruptive to the user experience, according to MDN Web Docs.

When a role="status" message is intended to be self-contained and not part of a larger, navigable section, a heading might not be necessary, says the W3C.

mgifford’s picture

@kthull failing an automated test isn't the end of the world. There are often false positives. Of course it is easier if the automated tests pass.

Interesting to consider though if role=status would be sufficient substitution. Worth looking into a bit more. Thanks!

thamas’s picture

Maybe I misunderstood something foundational, but as I see it, Drupal messages are generated on the server-side, and because of that, a whole page reload occurs before they are displayed. And reloading the page is "change of context" even if the "same" page is reloaded, isn't it?

So if this behaviour is "change of context", then it is totally irrelevant to use role="status" because

There are two main criteria that determine whether something meets the definition of a status message:

  • the message "provides information to the user on the success or results of an action, on the waiting state of an application, on the progress of a process, or on the existence of errors";
  • the message is not delivered via a change in context.

(Source: the same page I linked above)

Besides the content of the technical definition, it is also important what happens in practice: when the page reloads, a screen reader just goes "back" to the beginning of the page and handles it as new content. This situation requires a different approach: the focus should be automatically/programmatically shifted to the message (which is not a "Status message" by definition).

Of course, messages can be added with AJAX, or JavaScript.

For JS, we have Drupal.announce – that should be used.

In case of AJAX, the current suggestion (define two separate containers with "status" and "alert") roles may be correct, as I understand. But it is important that these containers must exist when the page loads, and they should not be generated together with the message only! (Maybe this is how it is meant to be already, but it was not clear to me from the current issue definition/discussion.)

A small note on role="error"* role="alert". This WCAG page says:

The purpose of this technique is to notify Assistive Technologies (AT) when an input error occurs.

Highlight from me. I'm not sure how serious "input error" is and what we can mean by "input error", but it is worth noting.

*There is no role="error" – I wrote this wrong initially. Adjusted based on comment #89.

mark_fullmer’s picture

And reloading the page is "change of context" even if the "same" page is reloaded, isn't it?

Hrmm. I'm not sure that any reloading of a page is in fact a "change of context," especially when it's the same page reloaded. From https://www.w3.org/WAI/WCAG21/Understanding/status-messages#dfn-changes-... , there are multiple reasons why it might not constitute a change in context:

major changes that, if made without user awareness, can disorient users who are not able to view the entire page simultaneously

A Drupal page reload resulting in a new status message *is* made with user awareness, since it would be triggered by a user action.

A change of content is not always a change of context. Changes in content, such as an expanding outline, dynamic menu, or a tab control do not necessarily change the context

The above seems more along the lines of a status message showing on the page, whereas the following example of a change in context does not sound like a status message showing on the page:

Opening a new window, moving focus to a different component, going to a new page (including anything that would look to a user as if they had moved to a new page) or significantly re-arranging the content of a page are examples of changes of context.

So it does seem that the Drupal status message meets the two criteria of the definition of a status message.

thamas’s picture

Thanks for your reply, Mark!

I was thinking about these too. And you may be right, and it is not a context change, reloading a page by the definition.

But that is why I did not end my comment with that.

When the page reloads, the browser rebuilds it from scratch (including the accessibility tree), right? If so that is a new page, new context even it contains the same things as before. Or am I wrong?

Anyway, it is just theory yet. :)

I have not seen any comments about testing the newer, suggested versions in practice with screen readers.

I have not done that myself either yet, but I plan to do it.

thamas’s picture

So I have not tested any of the patches above. Instead of that, I have updated the block--system-messages-block.html.twig template by adding <div role="status" aria-atomic="true"> around its content. Then I checked it with a screen reader (VoiceOver + Safari on Mac).

The results:

When the page was reloaded before displaying the Status message the screen reader did NOT announce the message.

When the Status message was added by AJAX (the Frontend Editing contrib module's toggle switch does that) the screen reader DID announce the status message.

(Of course, more detailed testing with more tools would be fine, but these results support what I wrote before.)

cwilcox808’s picture

When a page reloads (browser makes a page request and the server returns a page with the same URL), due to a form submission, following a link, etc. I would classify it as a change in context because focus changes, it's moved to the beginning of the page. Users expect a change in context when submitting a form or using a link.

@thamas Regarding your template update, role="status" implicitly has `aria-live="polite"` and `aria-atomic="true"` so neither attribute needs to be explicitly on the element. Also, correcting a previous message, there is no "error" role, that was meant to be "alert;" I think most messages classified as errors on Drupal are not input errors, certainly not the ones that only appear on page reload.

The purpose of role="status" is to create an ARIA live region, it's not meant to convey the nature of its contents or to aid in page navigation, other roles, like Landmark roles are for that. According to the ARIA specifications, status contents are not meant to be announced on page load, only when the live region contents change. I've found this to be the case in my testing. Navigating the page contents element-by-element, NVDA (with Chrome and Firefox) did not announce when a role="status" element was entered, exited, or even it was focused (even when the element had a name). When navigating using macOS 15.7.2 VoiceOver + Safari 26.1 it does choose to announce when the element was entered and exited when it had a name. Neither behavior is right or wrong, they're just different.

According to the ARIA specifications, `role="alert"` contents on page load should be announced but in practice I found they often aren't. macOS VoiceOver + Safari did not, NVDA + Firefox did not, NVDA + Chrome did, iOS 18.7.2 VoiceOver + Safari did but only the second alert when there were two alerts on the page (role="alert" is implicitly aria-live="assertive" which stops whatever a screen reader is saying to announce its contents, separate alerts are not queued).

thamas’s picture

Thanks for the information about real-world testing results @cwilcox808!

So, because the existing content of a live region is not announced after page reload (as that is not a content change of an existing page) I was thinking how it should be managed.

As I wrote before, when the page reloads before displaying itself, with the added message: "This situation requires a different approach: the focus should be automatically/programmatically shifted to the message (which is not a "Status message" by definition)."

I do not like that. And I was thinking: what if Drupal displayed all status messages with AJAX (even when the page needs a reload for any other reason)? This way, the message would be a content change of an existing page that can be handled by screen readers when it happens inside a live region (what is automatically created defining role="status").

Related feature in core: https://www.drupal.org/node/3086403 New MessageCommand allows adding messages from an Ajax response

(There used to be a contrib module forAJAX messages too: https://www.drupal.org/project/ajax_msgs)

What do you think about it?

thamas’s picture

Addition to my previous comment: TIL that htmx is now in Drupal core. So if my AJAX suggestion above is the way, it can be built on htmx.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

thamas’s picture

Title: Messages should have role=status instead of role=contentinfo » System messages should be accessible
thamas’s picture

Some notes to my "AJAX/htmx" messages idea by my teammate Artem Dmitriiev ("a.dmitriiev" here):

In Drupal 7 times, there was no lazy loading of blocks, but now there is starting with Drupal 8, and therefore the message are loaded separately from the main page already in case Big Pipe is enabled. But it is still doing it after page is loaded. There is another problem about messages with ajax, is that the actions the trigger the messages. If it is a form submission, and the form is not using ajax, there will be a page reload anyway, disregarding how message is retrieved.

mgifford’s picture

2dareis2do’s picture

Just a general observation around system messages. There is often a bit of a delay showing the notification and when it does appear there can be a bit of jump. If there are multiple notifications being shown this is more noticeable depending on your network connection, speed of the server, web browser etc.

mgifford’s picture

I stumbled on these issues when looking at the admin theme too https://www.drupal.org/project/drupal/issues/3587678

I like how the USWDS has done this:
https://designsystem.digital.gov/components/alert/

The UK has an issue in their issue queue from 2024 to implement something like this.

thamas’s picture

Thanks for the updates @mgifford. This topic is quite complex – but it would be nice to see some progress on it!