You may have recently read Adrian Roselli’s Scraping Burned Toast, Chris Coyier’s summary of the current “toast conversation”. Or maybe you’ve browsed the GitHub repository for A Standard ‘Toast’ UI Element and its WICG discussion thread.

Maybe you’re just familiar with “toasts” from reviewing Android development docs and component libraries like Material Design (I used to have a link to that component library here, but they updated their url without setting up a redirect. I’m sure you can google it well enough if you want…).

Or maybe, when you think of toast you just think about a common breakfast item, or a way to give a sandwich a bit more crunch. If this is you, you’re reading the wrong blog post. You can stop here.

For those of you who haven’t gone off to look for another article on how to make advocado toast (it’s… not hard), regardless of how familiar you are with the concept of a “toast” component, there was an attempt to pave the cow paths that different UI libraries have created for said (not actually) crispy bread. Unfortunately, the accessibility and inclusive UX of a toast component varies quite a bit depending on which component library you review. This did not help those trying to standardize this component, as the prior art was all rather dubious in authenticity, in regards to accessibility.

At the time I commented on this a bit in the previously mentioned GitHub issues. But, it got me thinking of how I might outline the functionality for a toast / messaging component. Here are some quick-ish thoughts on that…

A good toast is one that doesn’t stray

A toast component should deliver a concise message in the form of a consistently positioned popup. Often, a toast message appears from the bottom of a screen or viewport. This is particularly true for small screen devices. A “growler” notification could also be considered a type of “toast”. They generally appear from a top or bottom corner of the screen or viewport. Regardless of whatever a standardized positioning might be, authors would need to make sure that they were consistent in the positioning of their toasts. People don’t much appreciate having to guess where notifications are going to appear.

At a high level, toasts should be used to indicate the completion of a task or process initiated by the user or the application itself. For instance, a notification verifying the a file was saved, that a message had been sent, or a that a meeting is about to start or has started. A good toast doesn’t try to do too much. If your toast has multiple lines of text, or multiple CTAs, then guess what. You are trying to have a dialog. Treat it as such.

Time your toast appropriately. Record it for posterity

If someone were to ignore, or miss a toast message due to its timed display, there should be no negative impact on their current activities or the status that the message conveyed. Using the previous examples, ignoring a toast message would still mean that a file was saved, that a message was sent, or that a meeting could have been an email - I mean, is about to or is starting.

That said, what’s important to different individuals is not a constant. WCAG 2.2.1: Timing Adjustable should be considered in creating a toast component to ensure that all users can have adequate time to acknowledge a toast’s message. Outside of some feed-like (“snackbar”? do people consider toast a snack? Letting hungry people name web components makes us all sound ridiculous) implementations of toasts, where the messages may not remain visible but get appended to a log of notifications. Very few toast components take into account this Level A WCAG success criteria, or the larger UX benefit of being able to review or re-review notifications at one’s leisure.

Inclusive UX of a toast

A toast component should be considered a type of status message, and thus should be injected into a role="status". Or, if the toast messages will be ‘logged’ so that they can be reviewed later (repeat this idea enough maybe it’ll stick?), then they should be injected into a role="log" This will ensure that when a toast is displayed on screen, its contents will be “politely” communicated to assistive technologies, such as screen readers.

Genearlly, a toast component should not contain interactive controls. I realize people will continue to do this anyway - which is why I keep writing these words to talk about ways to mitigate for that. People will often do things that have dubious UX so long as it doesnt impact them… Anyway, as doing so can create a gap in truly inclusive UX, depending on how important the control in question is and thr race against time people usually have to put up with to reach it, the following are the concerns to try and mitigate:

Live regions do not convey the semantics of the content they announce. A status message containing the text “Your message was sent.” Followed by a link or button with the text “Undo”. Will be communicated as “Your message was sent. Undo”. Since the role semantics of “Undo” will not be announced, the following needs to be considered…

  • People using a screen reader can infer that the “Undo” may be an actionable element, but depending on the element used (again, link? or button?, lol other slop garbage one attached an onclick event to?) means that they’ll have to guess which element they should try to search for, if they need to reach said “Undo”. Being that a toast is typically an element that is automatically dismissed after a short time (again a WCAG 2.2.1 concern), these users will have to figure this out quickly, and hopefully navigate to the control in enough time before the message goes away, if they in fact need to “undo” something… such as that email they sent off saying that meeting they got a notification about could have been an email…
  • If a screen reader user does navigate to the Undo control in enough time, the toast could still dismiss. While JavaScript listeners could be written to “not allow for toast dismissal if keyboard focus or mouse hover is within/over the toast”, this wouldn’t apply to certain screen readers where virtual cursor navigation (i.e., “browse” or “scan” mode) doesn’t fire either of those events.
  • For sighted keyboard users, or for users who may be using a type of pointer device (mouse), but who have reduced mobility, they too are against a time limit for how quickly they can navigate through the DOM or move their pointer to the toast component.
  • If a user is able to surpass all the above mentioned points, authors still would need to then include a function to manage focus. Upon activating “Undo”, focus would need to return to a logical location (the submit button for the message?) For other instances of toasts that contain “close” buttons, focus would need to be managed here too, otherwise focus could be lost and users would have to return to the top of the current document and navigate back to where they last left off.
  • Another gap to consider is that a popup message of any kind can introduce obstruction issues for people who have zoomed in browsers, or smaller screen real estate in general. (Maybe use a media query to not display as a popup?)
  • For those who are using screen magnification software, but who are not using a screen reader, might be magnifying a portion of the page where the pop up message doesn’t appear. If there is an important control (again “Undo”) within this pop up, it could go completely unnoticed.
  • Finally, back on the topic of WCAG 2.2.1 Timing Adjustable, ideally there could be some sort of browser or OS level setting for the timing of messages to adhere to. Rather than setting an arbitrary time limit, the affordance for how long a toast message could display before dismissing (if at all) could be determined by end-users, similar to a high contrast or reduced motion setting can be respected.

Mitigating UX gaps for important interactive elements

If an action is important, and there’s no other means to perform said action, it should not be included within a toast component. But, if one were to want to work around the mentioned points of concern, and include something like an “undo” action within a toast, the following conditions should be true:

  • The contained action is also readily available outside of the toast, in the primary document. e.g., if an item from a list was deleted it should be replaced by an “undo” button, instead of a full deletion, so that the toast’s “undo” wouldn’t be the only way to undo said action.
  • Or, the toast message should be saved to a notification history page or component (see again ARIA’s log role).
  • Use a non-modal dialog to contain the message. You could even visually style and position the dialog to more closely resemble a “toast”, as defined by your style guidelines. While that may seem like a point of contention to “style a component to look like another component”… well, people don’t seem to have a problem styling links to look like buttons, and vice versa. Also, a “toast” doesn’t look anything like actual toast… so we’re already off to a horrible start with visuals aligning with names.

Popovers and toasts - breakfast carbs unite?

Since originally writing and updating this post back in 2019/2022, the popover attribute was added to the HTML Living Standard. One of the use cases for popovers was to create toast notifications. And while that’s all well and good, popover alone isn’t enough to make a toast component. Having been part of the discussions / development of that attribute, I wrote about the accessibility of the attribute and what it does, and doesn’t do for you.

IMO, the most relevant feature a popover brings to the table when creating a toast is that it allows it to be presented in the top layer. A standardized notification widget definitely would need this. But a popover is not a live region by default. It has no implicit role semantics by default (that would be meaningful for a toast). And presently, it cannot escape inertness - so invoking it from your primary document while a modal dialog is open means it won’t be exposed to AT, nor will any control in it be actional by anyone (again - inert).

There’s been talk of allowing elements in the top layer to escape inertness… and that might not be a bad idea. But that alone isn’t really enough for making an accessible toast notification.

To expand on that… a modal dialog is understood to mean that content outside of it is meant to be inert. Making a popover not inert breaks that expectation. Screen readers, for instance, will often constrain their virtual cursors to the confines of a modal dialog. Even if a toast/popover were to be presented outside of it as another not inert element, that doesn’t necessarily mean the content of the toast will be accessible via screen readers. If you’re curious about what I mean, try checking out this codepen with JAWS and see how it behaves.

Rather, what would be ‘best’, IMO, is that we built on the primitives that make popover and use those to create a new “toast” / “message” element that could escape being inert, present itself in the top layer, and as far as the a11y tree is concerned, be considered a child / last child element of a rendered modal dialog.

Then, there’d be nothing “outside” of the modal dialog to need to be made aware of. Default live region support could be implemented into the element, and it could be presented in the top layer to ensure it appears above all other content. So you’ll be less likely to miss the meeting that you don’t want to attend has started.

Wrapping up

I’m sure that people could poke (keep poking) at this a bit more to come up with other UX considerations and functionality that a toast / messaging component should have. I hope you do. Maybe make an open ui issue if you don’t see something there that already covers what you’d want to talk about.