Notifications
Notifications are used to inform users about the important events that occur dynamically. These may include alerts, error messages, feedback messages and so on.
NOTE:
- New to accessibility or uncertain of requirements, it will be helpful to review all sections below.
- Already familiar with requirements, skip to the “Working Example” section for sample HTML, CSS and JavaScript (when needed), along with a working demo.
-
Notifications CAN remain for a certain interval of time or persistent.
- The notification SHOULD NOT disappear in a very short interval of time. At least it should stay for minimum of 5 seconds. If the notification consist of larger message, the interval SHOULD be increased further.
- The focus SHOULD NOT move to the notification container in case the notification disappear after a certain interval.
-
In case wherein the notification stays persistent, there SHOULD be a mechanism
to dismiss the notification. For example, a close (x) button SHOULD be provided
to dismiss the notification.
-
The button MUST be defined using native HTML
<button>
element -
The button SHOULD be provided with an accessible name using
aria-label
attribute considering the icon is defined using CSS content property. If the X icon is defined using<img>
element appropriate textual description SHOULD be provided usingalt
attribute. If the X icon is defined as SVG image, an appropriate textual description SHOULD be defined usingrole="img"
along witharia-label
attribute on the<svg>
element. - The X icon SHOULD meet the color contrast ratio requirement of 3:1 with the background color in different states like default, hover and focus.
- Once the notification is dismissed using Close (x) button, the focus MUST go to the next logical element in case if the original element that triggered this notification is no longer available OR it SHOULD go back to the triggering element of the notification.
- The focus SHOULD move to the notification container as soon as it appears.
-
The button MUST be defined using native HTML
- Notifications CAN be made available to assistive technologies using live regions.
- The color contrast requirement of 4.5:1 ratio for small text and 3:1 for large text (text that is at least 18 points or 14 points and bold) MUST be met with the adjacent color for the notification text.
- The color contrast requirement of 3:1 ratio MUST be met with the adjacent color for icons such as those used to indicate error, warnings and so on.
- Assistive technologies like screen readers MUST announce the notifications as soon as they appear on the page.
Native HTML is the preferred method to implement notifications.
-
The notifications CAN be implemented in the form of native JavaScript alert box. These
can be implemented using
window.alert()
method. - An alert box prompts the user to give a response to an action to be performed such as Yes/No OR Ok/Cancel and so on.
- In some cases, the alert box CAN be dismissed by pressing Escape key.
Note: This is not used widely now-a-days in modern website development as the notifications now-a-days are designed to be more appealing as per the purpose.
Notifications CAN be implemented using live regions.
Roles
- Alert: It is used when the notification needs immediate user attention without interrupting the users’ task. It has an implicit aria-live value of assertive and aria-atomic value of true. For example - a warning message that appears when a user is about to delete data.
- Status: It is used when the notification is not important enough to bring immediate user attention. It has an implicit aria-live value of polite and aria-atomic value of true. For example, if any chapter is bookmarked, a status message appears that indicates that chapter has been bookmarked successfully. Another example would be an error message that appears at the top of the form that indicates a summary of error that has occurred for the form.
Attributes:
- aria-atomic: It is used to indicate if assistive technologies will present all or parts of the dynamic updates. Its value is set true when the entire region is to be presented by assistive technologies. Its value is set to false when only the updated content is to be presented by assistive technologies.
- aria-busy: It is used to instruct assistive technologies that an element is being modified and need to wait till modification is completed before presenting to the user. Its value is set to true to indicate the element is being modified. Its value is set to false in order to indicate that there are no modifications pending.
- aria-live: It is used to indicate that an element will be updated, and describe the types of updates the user agents, assistive technologies, and user can expect from the live region.
-
Its value CAN be set to
- assertive – Has the highest priority and is presented to the user immediately.
- polite – Updates to the region is presented to the user when the user is in idle state.
- off – Updates to the live region is not intimated to the user unless the focus is on the region.
- A live region CAN be defined to any container where the dynamic updates are going to happen.
- The live region MUST be there within the DOM by default wherein the notification messages will get injected.
- A page MUST not contain multiple live regions where dynamic updates are happening simultaneously.
There are more such roles and attributes that CAN be used for marking live regions that would consist of different types of dynamic updates occurring in different scenarios. More information on this will be covered in “Live Regions” component.
For example,
<!-- Notification container in default state -->
<div class="toast-container">
<div class="toast-body" role="alert" tabindex="-1">
</div>
</div>
<!-- Notification container when the notification appears -->
<div class="toast-container">
<div class="toast-body success active" role="alert" tabindex="-1">
Thank you for subscribing
<button class="closebtn" type="button" aria-label="Remove notification">×</button>
</div>
</div>
<!-- Start of form -->
<form method="post" id="subscribe">
<div class="button-container">
<label for="email">Email:</label>
<input type="email" id="email" autocomplete="email" name="email">
<small class="error" id="err"></small>
<button id="button-success" class="success">Subscribe</button>
</div>
</form>
A well-defined notification benefits majorly the below users.
- People with cognitive disabilities
- People using speech input
- People with limited dexterity
- People using keyboard only
- People using screen readers
<div class="toast-container cancel">
<div class="toast-body" role="alert" tabindex="-1">
</div>
</div>
<form method="post" id="subscribe">
<div class="button-container">
<label for="email">Email:</label>
<input type="email" id="email" autocomplete="email" name="email">
<small class="error" id="err"></small>
<button id="button-success" class="success">Subscribe</button>
</div>
</form>
:root {
--toast-container-width: 100%;
--toast-body-width: 250px;
}
.toast-container {
position: absolute;
top: 15%;
right: 0px;
width: var(--toast-container-width);
display: flex;
align-items: center;
justify-content: center;
overflow-x: hidden;
}
.toast-body {
width: var(--toast-body-width);
height: 60px;
transition: margin 1s;
border-radius: 18px;
z-index: 1000;
margin: 0px calc(-1 * var(--toast-body-width)) 0px 0px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.error {
background-color: #ffffff;
color: #cc0000;
font-size: 18px;
}
.success {
background-color: #003057;
color: #ffffff;
font-size: .85rem
}
.active {
margin: 0px 10px 0px 0px;
padding: 0 5px;
}
.button-container {
margin: 30vh 0px 0px;
width: 100%;
height: 131px;
display: grid;
justify-content: center;
align-items: center;
}
input {
padding: 12px;
border-radius: 4px;
display: block;
border: 1px solid #4f4f4f;
font-size: 20px;
color: #000 !important;
}
button {
border: none;
padding: 8px;
display: block;
border-radius: 10px;
cursor: pointer;
}
label {
font-size: 24px;
}
.closebtn {
margin-left: 15px;
background: #003057;
color: white;
font-weight: bold;
float: right;
font-size: 22px;
line-height: 20px;
cursor: pointer;
position: relative;
z-index: 1000;
transition: 0.3s;
}
.cancel {
top: -100%;
height: 0;
}
let EMAIL_REQUIRED = "Please enter your email";
let EMAIL_INVALID = "Please enter a correct email address format";
let toastBody = document.getElementsByClassName("toast-body")[0];
let showSuccessToastButton = document.getElementById("button-success");
let forminput = document.getElementById("email");
function showMessage(input, message, type) {
let emailsub = input;
let msg = input.parentNode.querySelector("small");
msg.innerText = message;
input.className = type ? "success" : "error";
if (!type) {
emailsub.setAttribute('aria-describedby','err')
emailsub.focus();
} else {
emailsub.classList.remove("success");
}
return type;
}
function showError(input, message) {
return showMessage(input, message, false);
}
function showSuccess(input) {
return showMessage(input, "", true);
}
function hasValue(input, message) {
if (input.value.trim() === "") {
return showError(input, message);
}
return showSuccess(input);
}
function validateEmail(input, requiredMsg, invalidMsg) {
if (!hasValue(input, requiredMsg)) {
return false;
}
let emailRegex =
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
let email = input.value.trim();
if (!emailRegex.test(email)) {
return showError(input, invalidMsg);
}
return true;
}
let form = document.querySelector("#subscribe");
form.addEventListener("submit", function (event) {
event.preventDefault();
let emailValid = validateEmail(form.elements["email"], EMAIL_REQUIRED, EMAIL_INVALID);
closebtn = document.getElementsByClassName("closebtn");
if (emailValid) {
toastBody.innerHTML = "";
toastBody.classList.remove("error");
toastBody.classList.add("success");
toastBody.classList.add("active");
toastBody.innerHTML = "Thank you for subscribing";
toastBody.focus();
let newButton = document.createElement("BUTTON");
newButton.classList.add('closebtn');
newButton.innerHTML = "×";
newButton.setAttribute('type', 'button');
newButton.setAttribute('aria-label', 'Remove notification');
newButton.addEventListener("click", () => {
toastBody.classList.remove("active");
toastBody.parentElement.classList.add("cancel");
forminput.value="";
forminput.focus();
});
toastBody.appendChild(newButton);
showSuccessToastButton.disabled = true;
toastBody.parentElement.classList.remove("cancel");
showSuccessToastButton.disabled = false;
}
});