Toast Notifications

Toast Notifications

A thoughtful work for a seemingly small and common component, that surprisingly doesn't even have a standard!

Toasts are those tiny boxed messages with (mainly, but not only) success or error notifications that appear on one side of the screen. Their nature is (and should remain) non-intrusive: they have to bring the user a brief system notification and disappear after a while. The gist is that if you miss a toast, this should not impact negatively your activity.

Toast must contain a text, but it can also be useful having an icon, a title, and a “quick dismiss system” (the easier way I can think of is a button) to get rid of 'em manually before they time out. In some cases also a CTA button or link could be needed.

The role for the single toast should be "status" or eventually "log", since messages are presented in a specific chronological order (by default a log has aria-live="polite") and could be potentially reviewed later.

If you ask me, this is literally begging for an “ordered list” container, capable of naturally stacking its items... Obviously, you'll need JS to add and remove list items from the <ol>, which underlines its natural role of wrapper for its items. See? No need for workarounds or <div> annidation: just the right tool for the specific job.

The container

<ol id="toast-center" class="toast__wrapper" type="list" role="region" aria-label="Toast notifications">
...your toasts here...
</ol>

For consistency, it is required that toasts appear always in the same place (both in the viewport and tab order point of view), and must not obstruct elements of primary role or other critical elements. Probably the better place is the top-right corner or, even better, the top-center, to also avoid potential issues with screen magnifiers.

In "code perspective" it's important that toasts are located at the end of the page: in this way users will not find them suddenly in the middle of content (and this also helps to absolute position it). You could also put them at the beginning, but in this way it would be another chunk of content you have to skip before reaching the main content.

As the last thing, giving the container a role="region", with relative aria-label, lets the user easily jump to the section using screen readers

The items

<li role=”log” class=”toast”>
    <span class=”toast__icon toast__iconsuccess”>Success</span>
    <span class=”toast__content”>Operation done!</span>
    <button class=”toast__cta” type=”button”>Action</button>
    <button class=”toast__dismiss” type=”button” aria-label=”Dismiss this toast”>X</button>
</li>

There is not any specification for the single toast content, so what you see here is just an example, taking into account all the fields that could be useful (I've grouped them with a BEM naming for ease of both styling and grouping classes).

Items have to be announced by screen readers, so the way you manage aria-live attributes is a key factor. Here I opted for role="log", that implies an aria-live="polite". To work properly aria-live should be present at page first load. This behaviour is not so obvious if you think that your JS should add toasts only when needed! Try this approach, since I think it's the most appropriate, but in case of malfunctioning, try extending the logic, also having an empty container with aria-live="polite" and aria-atomic="true": this could do the job.

By the way, in deciding which role is suitable for your toasts, you can refer to the page Understanding Success Criterion 4.1.3: Status Messages | WAI | W3C, where it is stated to use:

  • role="status" if the message advises on the success or results of an action or the state of an application;

  • role="alert" if the message conveys a suggestion or a warning on the existence of an error;

  • role="log" If a status message conveys information on the progress of a process:

I here deduce that the developer/content manager should be able to change the role depending on what kind of message content should be announced, while keeping their aspect essentially the same. For an explanation of aria-live attribute you can have a look at Aria-live: announce DOM changes article.

The style

Given all the accessibility guidelines about texts and colors, each type of toast should have a different kind of color: colors convey a message by themselves and here I suggest using classic ones instead of trying weird design choices. The more common and direct the message, the better. The background could be slightly (slightly, I said!) transparent and blurred, to emphasise its being on top of the contents below.

And about the "being on top" don't forget to wisely use your position and z-index in your styles.

Focus management

The basic principle is that toast must not steal focus: this would abruptly interrupt a user in the mids of an action, and that's a terrible user experience. Announcing the message and letting the user continue with the action is enough.

As defined by WCAG, one of the criteria for a status message is that "the message is not delivered via a change in context".

No problem for mouse users, and for those using a screen reader leveraging the role="region"/aria-label it's easy to jump straight to the toast section.

When the user reaches the toast area, focus will follow along. In case of toast dismissal, focus SHOULD remain there, shifting automatically to the next toast. Make sure it works as expected, don't trust the correctness of the method alone!

Animations

Providing an animation when dismissing a toast could be a nice touch, both for design and accessibility. A slow fade could be beneficial also for some categories of impaired users, while for others, that prefer reduced animations, you could make use of the media query "prefers-reduced-motion", to entirely cut off the animation. Since these declarations are triggered by a user choice, you're good to go, giving both possibilities and letting the user choose between them.

Time limit

If you're thinking about a simple timeout before dismissing toasts I'm afraid I have some bad news for you, since you have to take into account that you should provide the possibility from the user to set the timeout length.

You have however to give impaired users adequate time to interact with timed content whenever possible, because they may require more time to read. Consider that ten times the default time has been chosen based on clinical experience and other guidelines and 20 seconds should be sufficient for almost all users (including those with spasticity) to hit 'any switch'. In the example you'll find at the end, I've opted for a mixed approach: if not specified, a timeout duration proportional to the length of all the text will be used, but only if no "dismiss button" has been required.

The easier approach could be to make toast static, requiring manual intervention to be dismissed. Evaluate carefully this decision, 'cause if you think you'll have a bunch of them, you'll risk filling the viewport with messages, covering all the rest! However, if the region where the toasts are is properly named (or has a properly set title), a screen reader user can easily navigate directly to it and dismiss the messages, while a sighted user has plenty of time to remove them.

Why this uncertainty?

Because at the moment of writing this article there isn't a real standard about toasts. Honestly, there isn't a name universally accepted for toasts either! Different frameworks interpret toasts differently, and specify different characteristics for 'em: they all agree only on the fact that a toast has a “content”. Not very helpful, right? (more info about the comparison at std-toast/study-group at master · jackbsteinberg/std-toast · GitHub).

Limitations and inconsistency between various screen readers don't help, as for many other elements, and the fact that an aria-live should exist in the DOM before its content changes in order to be announced, well, to me is like adding fuel to a fire...

I never thought these little messages could take so much effort, the first time I worked with them. A real rabbit hole! Obviously, any suggestion to reach a better level of accessibility is warmly welcome: let's work on it together.