Appearance
Keyboard Navigation
Single, dismiss-only action
As mentioned in Built-in Notifications / Accessibility, built-in notifications cannot be focused using Tab
.
If you have custom components with only one dismissal action and specific duration, you might want to apply the same logic and set its tabindex
to -1
.
Multiple actions
For custom notifications with multiple actions, proper keyboard navigation should be enabled for all users. These notifications typically have a duration set to Infinity
, preventing automatic dismissal.
Notivue provides a hassle-free component, enabling proper keyboard navigation and tab management for custom components with multiple actions.
This seamlessly combines with configuration options like queue
, limit
and pauseOnTabChange
and with NotivueSwipe
. It works with any duration
setting.
💡 Test this feature on the demo website by pushing some
Headless - Actions
notifications and then pressingTab
after the first one is displayed.
1. Configure the stream
Wrap Notivue with NotivueKeyboard and pass the exposed containersTabIndex
prop:
vue
<script setup>
import { NotivueKeyboard } from 'notivue'
import NotificationWithActions from './NotificationWithMultipleActions.vue'
</script>
<template>
<NotivueKeyboard v-slot="{ containersTabIndex }">
<Notivue v-slot="item" :containersTabIndex="containersTabIndex">
<NotificationWithActions :item="item" />
</Notivue>
</NotivueKeyboard>
<RouterView />
</template>
containersTabIndex
is a reactive object that dinamically toggles tab focusing for notifications with two or more focusable elements and disables it for any others that dont't meet the criteria.
2. Configure your component
The last step is to perform the same operation to any focusable elements inside your notification using useNotivueKeyboard
and style them accordingly.
vue
<script setup lang="ts">
// ...
import { useNotivueKeyboard } from 'notivue'
const { elementsTabIndex } = useNotivueKeyboard()
</script>
<template>
<div class="Notification">
<p :role="item.ariaRole" :aria-live="item.ariaLive">
{{ item.message }}
</p>
<nav>
<button @click="someCallback" :tabIndex="elementsTabIndex">Deny</button>
<button @click="someCallback" :tabIndex="elementsTabIndex">Accept</button>
</nav>
</div>
</template>
<style scoped>
[data-notivue-container]:focus-visible .Notification {
outline: 4px solid royalblue;
}
button:focus-visible {
outline: 2px solid royalblue;
outline-offset: 2px;
}
</style>
Then, somewhere in your global CSS, remove the outline from the container:
css
[data-notivue-container]:focus-visible {
outline: none;
}
Navigation overview
1. Entering the stream
NotivueKeyboard
observes the stream and looks for notifications that have 2 or more focusable elements and automatically flags them as candidates for keyboard navigation.
If the user is not navigating the stream, as soon as a new candidate is pushed, it can be focused by just pressing Tab
.
If the user is already navigating, the focus is automatically moved to the new candidate (at the top of the stream).
2. Leaving the stream
Users can exit the stream by either:
- Pressing
Shift + Tab
on the first notification - Pressing
Tab
on the last focusable element - Pressing
CTRL+N
- Pressing
Escape
- Clicking with the mouse outside the stream
In any of the above scenarios, users are notified with a push.info()
notification that the stream can be accessed again using CTRL+N
.
3. Re-entering the stream
- If new notifications are available, pressing
Tab
(or CRTL+N) is enough to re-enter the stream. - If no new notifications are available, but the previous ones are still displayed, users can re-enter the stream by pressing
CTRL+N
. - If the stream is empty and users attempt to enter it using
CTRL+N
, they'll be notified accordingly with anotherpush.info
notification.
Touch Devices
The above mentioned flow is designed for keyboard interaction and won't have any effect nor interfere with touch devices.
Simply remember to set their duration to Infinity
and let users engage naturally.
Customization - Props
You can customize the announcements and define a define a different key to be used in conjuction with CTRL
:
vue
<script setup>
const leaveMessage =
"You're leaving the notifications stream. Press CTRL+N to navigate it again."
const emptyMessage = 'No notifications to navigate.'
</script>
<template>
<NotivueKeyboard
comboKey="N"
:handleClicks="true"
:leaveMessage="leaveMessage"
:emptyMessage="emptyMessage"
:renderAnnouncement="true"
:maxAnnouncements="3"
>
<!-- ... -->
</NotivueKeyboard>
</template>
handleClicks
is enabled by default and handles the behavior after the user presses withSpace/Enter
a link or a button (assuming that it also dismisses the notification).If there's a next notification available, it will focus its container, if not it will exit the stream and announce the
leaveMessage
.renderAnnouncement
is enabled by default and renders the announcement as apush.info()
notification. If set tofalse
, the announcement will only be made available to screen readers.
Check the API Reference for more details.
Customization - Announcement component
When announcements are made, NotivueKeyboard pushes an info
notification to the stream.
To use a dedicated component for those notifications, you can do so by leveraging isNotivueKeyboard
push prop.
vue
<script setup>
import CustomAnnouncement from './CustomAnnouncement.vue'
import MyCustomNotification from './MyCustomNotification.vue'
</script>
<template>
<NotivueKeyboard v-slot="{ containersTabIndex }">
<Notivue v-slot="item" :containersTabIndex="containersTabIndex">
<CustomAnnouncement v-if="item.props.isNotivueKeyboard" :item="item" />
<MyCustomNotification :item="item" v-else />
</Notivue>
</NotivueKeyboard>
<!-- ... -->
</template>