Tailwind CSS and Alpine JS Toast Notification
Toast notifications are a great way to provide quick feedback to users. They can be used for success messages, error alerts, warnings, or general information. Toasts appear briefly, allowing users to stay informed without interrupting their workflow.
This component requires Alpine JS v3 to function properly. Some advanced features may require additional Alpine plugins (such as focus).
Tell Me MoreStacking Toast Notification
A collection of toast notifications that stack on top of each other to display multiple notifications.
The component listens for the 'notify' event and renders the notification using event details. The trigger buttons are examples of how to dispatch the events, but you can trigger the 'notify' event using JavaScript in any way you prefer.
Classic vs Modern Code Style
The difference between the two versions is how they're written. The classic version uses older-style classes like 'text-red-500' for styling, while the modern version, uses CSS variables and semantic names like 'text-primary' for theming. It's important to note that 'Classic' doesn't mean an older version—they both use Tailwind V4. Tell me more.
<!-- Triggers -->
<!-- Message Trigger -->
<button x-on:click="$dispatch('notify', { variant: 'message', sender:{name:'Jack Ellis', avatar:'https://penguinui.s3.amazonaws.com/component-assets/avatar-2.webp'}, message: 'Hey, can you review the PR I just submitted? Let me know if you spot any issues!' })" type="button" class="">Message</button>
<!-- Info Trigger -->
<button x-on:click="$dispatch('notify', { variant: 'info', title: 'Update Available', message: 'A new version of the app is ready for you. Update now to enjoy the latest features!' })" type="button" class="">Info</button>
<!-- Success Trigger -->
<button x-on:click="$dispatch('notify', { variant: 'success', title: 'Success!', message: 'Your changes have been saved. Keep up the great work!' })" type="button" class="">Success</button>
<!-- Danger Trigger -->
<button x-on:click="$dispatch('notify', { variant: 'danger', title: 'Oops!', message: 'Something went wrong. Please try again. If the problem persists, we’re here to help!' })" type="button" class="">Danger</button>
<!-- Warning Trigger -->
<button x-on:click="$dispatch('notify', { variant: 'warning', title: 'Action Needed', message: 'Your storage is getting low. Consider upgrading your plan.' })" type="button" class="">Warning</button>
<!-- Notifications -->
<div x-data="{
notifications: [],
displayDuration: 8000,
soundEffect: ,
addNotification({ variant = 'info', sender = null, title = null, message = null}) {
const id = Date.now()
const notification = { id, variant, sender, title, message }
// Keep only the most recent 20 notifications
if (this.notifications.length >= 20) {
this.notifications.splice(0, this.notifications.length - 19)
}
// Add the new notification to the notifications stack
this.notifications.push(notification)
if (this.soundEffect) {
// Play the notification sound
const notificationSound = new Audio('https://res.cloudinary.com/ds8pgw1pf/video/upload/v1728571480/penguinui/component-assets/sounds/ding.mp3')
notificationSound.play().catch((error) => {
console.error('Error playing the sound:', error)
})
}
},
removeNotification(id) {
setTimeout(() => {
this.notifications = this.notifications.filter(
(notification) => notification.id !== id,
)
}, 400);
},
}" x-on:notify.window="addNotification({
variant: $event.detail.variant,
sender: $event.detail.sender,
title: $event.detail.title,
message: $event.detail.message,
})">
<div x-on:mouseenter="$dispatch('pause-auto-dismiss')" x-on:mouseleave="$dispatch('resume-auto-dismiss')" class="">
<template x-for="(notification, index) in notifications" x-bind:key="notification.id">
<!-- root div holds all of the notifications -->
<div>
<!-- Info Notification -->
<template x-if="notification.variant === 'info'">
<div x-data="{ isVisible: false, timeout: null }" x-cloak x-show="isVisible" class="" role="alert" x-on:pause-auto-dismiss.window="clearTimeout(timeout)" x-on:resume-auto-dismiss.window=" timeout = setTimeout(() => {(isVisible = false), removeNotification(notification.id) }, displayDuration)" x-init="$nextTick(() => { isVisible = true }), (timeout = setTimeout(() => { isVisible = false, removeNotification(notification.id)}, displayDuration))" x-transition:enter="transition duration-300 ease-out" x-transition:enter-end="translate-y-0" x-transition:enter-start="translate-y-8" x-transition:leave="transition duration-300 ease-in" x-transition:leave-end="-translate-x-24 opacity-0 md:translate-x-24" x-transition:leave-start="translate-x-0 opacity-100">
<div class="">
<!-- Icon -->
<div class="" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="" aria-hidden="true">
<path fill-rule="evenodd" d="M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0Zm-7-4a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM9 9a.75.75 0 0 0 0 1.5h.253a.25.25 0 0 1 .244.304l-.459 2.066A1.75 1.75 0 0 0 10.747 15H11a.75.75 0 0 0 0-1.5h-.253a.25.25 0 0 1-.244-.304l.459-2.066A1.75 1.75 0 0 0 9.253 9H9Z" clip-rule="evenodd" />
</svg>
</div>
<!-- Title & Message -->
<div class="">
<h3 x-cloak x-show="notification.title" class="" x-text="notification.title"></h3>
<p x-cloak x-show="notification.message" class="" x-text="notification.message"></p>
</div>
<!--Dismiss Button -->
<button type="button" class="" aria-label="dismiss notification" x-on:click="(isVisible = false), removeNotification(notification.id)">
<svg xmlns="http://www.w3.org/2000/svg viewBox="0 0 24 24 stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</template>
<!-- Success Notification -->
<template x-if="notification.variant === 'success'">
<div x-data="{ isVisible: false, timeout: null }" x-cloak x-show="isVisible" class="" role="alert" x-on:pause-auto-dismiss.window="clearTimeout(timeout)" x-on:resume-auto-dismiss.window=" timeout = setTimeout(() => {(isVisible = false), removeNotification(notification.id) }, displayDuration)" x-init="$nextTick(() => { isVisible = true }), (timeout = setTimeout(() => { isVisible = false, removeNotification(notification.id)}, displayDuration))" x-transition:enter="transition duration-300 ease-out" x-transition:enter-end="translate-y-0" x-transition:enter-start="translate-y-8" x-transition:leave="transition duration-300 ease-in" x-transition:leave-end="-translate-x-24 opacity-0 md:translate-x-24" x-transition:leave-start="translate-x-0 opacity-100">
<div class="">
<!-- Icon -->
<div class="" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm3.857-9.809a.75.75 0 0 0-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 1 0-1.06 1.061l2.5 2.5a.75.75 0 0 0 1.137-.089l4-5.5Z" clip-rule="evenodd" />
</svg>
</div>
<!-- Title & Message -->
<div class="">
<h3 x-cloak x-show="notification.title" class="" x-text="notification.title"></h3>
<p x-cloak x-show="notification.message" class="" x-text="notification.message"></p>
</div>
<!--Dismiss Button -->
<button type="button" class="" aria-label="dismiss notification" x-on:click="(isVisible = false), removeNotification(notification.id)">
<svg xmlns="http://www.w3.org/2000/svg viewBox="0 0 24 24 stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</template>
<!-- Warning Notification -->
<template x-if="notification.variant === 'warning'">
<div x-data="{ isVisible: false, timeout: null }" x-cloak x-show="isVisible" class="" role="alert" x-on:pause-auto-dismiss.window="clearTimeout(timeout)" x-on:resume-auto-dismiss.window=" timeout = setTimeout(() => {(isVisible = false), removeNotification(notification.id) }, displayDuration)" x-init="$nextTick(() => { isVisible = true }), (timeout = setTimeout(() => { isVisible = false, removeNotification(notification.id)}, displayDuration))" x-transition:enter="transition duration-300 ease-out" x-transition:enter-end="translate-y-0" x-transition:enter-start="translate-y-8" x-transition:leave="transition duration-300 ease-in" x-transition:leave-end="-translate-x-24 opacity-0 md:translate-x-24" x-transition:leave-start="translate-x-0 opacity-100">
<div class="">
<!-- Icon -->
<div class="" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="" aria-hidden="true">
<path fill-rule="evenodd" d="M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0Zm-8-5a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-1.5 0v-4.5A.75.75 0 0 1 10 5Zm0 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" clip-rule="evenodd" />
</svg>
</div>
<!-- Title & Message -->
<div class="">
<h3 x-cloak x-show="notification.title" class="" x-text="notification.title"></h3>
<p x-cloak x-show="notification.message" class="" x-text="notification.message"></p>
</div>
<!--Dismiss Button -->
<button type="button" class="" aria-label="dismiss notification" x-on:click="(isVisible = false), removeNotification(notification.id)">
<svg xmlns="http://www.w3.org/2000/svg viewBox="0 0 24 24 stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</template>
<!-- Danger Notification -->
<template x-if="notification.variant === 'danger'">
<div x-data="{ isVisible: false, timeout: null }" x-cloak x-show="isVisible" class="" role="alert" x-on:pause-auto-dismiss.window="clearTimeout(timeout)" x-on:resume-auto-dismiss.window=" timeout = setTimeout(() => {(isVisible = false), removeNotification(notification.id) }, displayDuration)" x-init="$nextTick(() => { isVisible = true }), (timeout = setTimeout(() => { isVisible = false, removeNotification(notification.id)}, displayDuration))" x-transition:enter="transition duration-300 ease-out" x-transition:enter-end="translate-y-0" x-transition:enter-start="translate-y-8" x-transition:leave="transition duration-300 ease-in" x-transition:leave-end="-translate-x-24 opacity-0 md:translate-x-24" x-transition:leave-start="translate-x-0 opacity-100">
<div class="">
<!-- Icon -->
<div class="" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="" aria-hidden="true">
<path fill-rule="evenodd" d="M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0Zm-8-5a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-1.5 0v-4.5A.75.75 0 0 1 10 5Zm0 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" clip-rule="evenodd" />
</svg>
</div>
<!-- Title & Message -->
<div class="">
<h3 x-cloak x-show="notification.title" class="" x-text="notification.title"></h3>
<p x-cloak x-show="notification.message" class="" x-text="notification.message"></p>
</div>
<!--Dismiss Button -->
<button type="button" class="" aria-label="dismiss notification" x-on:click="(isVisible = false), removeNotification(notification.id)">
<svg xmlns="http://www.w3.org/2000/svg viewBox="0 0 24 24 stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</template>
<!-- Message Notification -->
<template x-if="notification.variant === 'message'">
<div x-data="{ isVisible: false, timeout: null }" x-cloak x-show="isVisible" class="" role="alert" x-on:pause-auto-dismiss.window="clearTimeout(timeout)" x-on:resume-auto-dismiss.window="timeout = setTimeout(() => { isVisible = false, removeNotification(notification.id) }, displayDuration)" x-init="$nextTick(() => { isVisible = true }), (timeout = setTimeout(() => { isVisible = false, removeNotification(notification.id) }, displayDuration))" x-transition:enter="transition duration-300 ease-out" x-transition:enter-end="translate-y-0" x-transition:enter-start="translate-y-8" x-transition:leave="transition duration-300 ease-in" x-transition:leave-end="-translate-x-24 opacity-0 md:translate-x-24" x-transition:leave-start="translate-x-0 opacity-100">
<div class="">
<div class="">
<!-- Avatar -->
<img x-cloak x-show="notification.sender.avatar" class="" alt="avatar" aria-hidden="true" x-bind:src="notification.sender.avatar"/>
<div class="">
<!-- Title & Message -->
<h3 x-cloak x-show="notification.sender.name" class="" x-text="notification.sender.name"></h3>
<p x-cloak x-show="notification.message" class="" x-text="notification.message"></p>
<!-- Action Buttons -->
<div class="">
<button type="button" class="">Reply</button>
<button type="button" class="" x-on:click=" (isVisible = false), setTimeout(() => { removeNotification(notification.id) }, 400)">Dismiss</button>
</div>
</div>
</div>
<!-- Dismiss Button -->
<button type="button" class="" aria-label="dismiss notification" x-on:click="(isVisible = false), removeNotification(notification.id)">
<svg xmlns="http://www.w3.org/2000/svg viewBox="0 0 24 24 stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</template>
</div>
</template>
</div>
</div>
<!-- Triggers -->
<!-- Message Trigger -->
<button x-on:click="$dispatch('notify', { variant: 'message', sender:{name:'Jack Ellis', avatar:'https://penguinui.s3.amazonaws.com/component-assets/avatar-2.webp'}, message: 'Hey, can you review the PR I just submitted? Let me know if you spot any issues!' })" type="button" class="">Message</button>
<!-- Info Trigger -->
<button x-on:click="$dispatch('notify', { variant: 'info', title: 'Update Available', message: 'A new version of the app is ready for you. Update now to enjoy the latest features!' })" type="button" class="">Info</button>
<!-- Success Trigger -->
<button x-on:click="$dispatch('notify', { variant: 'success', title: 'Success!', message: 'Your changes have been saved. Keep up the great work!' })" type="button" class="">Success</button>
<!-- Danger Trigger -->
<button x-on:click="$dispatch('notify', { variant: 'danger', title: 'Oops!', message: 'Something went wrong. Please try again. If the problem persists, we’re here to help!' })" type="button" class="">Danger</button>
<!-- Warning Trigger -->
<button x-on:click="$dispatch('notify', { variant: 'warning', title: 'Action Needed', message: 'Your storage is getting low. Consider upgrading your plan.' })" type="button" class="">Warning</button>
<!-- Notifications -->
<div x-data="{
notifications: [],
displayDuration: 8000,
soundEffect: ,
addNotification({ variant = 'info', sender = null, title = null, message = null}) {
const id = Date.now()
const notification = { id, variant, sender, title, message }
// Keep only the most recent 20 notifications
if (this.notifications.length >= 20) {
this.notifications.splice(0, this.notifications.length - 19)
}
// Add the new notification to the notifications stack
this.notifications.push(notification)
if (this.soundEffect) {
// Play the notification sound
const notificationSound = new Audio('https://res.cloudinary.com/ds8pgw1pf/video/upload/v1728571480/penguinui/component-assets/sounds/ding.mp3')
notificationSound.play().catch((error) => {
console.error('Error playing the sound:', error)
})
}
},
removeNotification(id) {
setTimeout(() => {
this.notifications = this.notifications.filter(
(notification) => notification.id !== id,
)
}, 400);
},
}" x-on:notify.window="addNotification({
variant: $event.detail.variant,
sender: $event.detail.sender,
title: $event.detail.title,
message: $event.detail.message,
})">
<div x-on:mouseenter="$dispatch('pause-auto-dismiss')" x-on:mouseleave="$dispatch('resume-auto-dismiss')" class="">
<template x-for="(notification, index) in notifications" x-bind:key="notification.id">
<!-- root div holds all of the notifications -->
<div>
<!-- Info Notification -->
<template x-if="notification.variant === 'info'">
<div x-data="{ isVisible: false, timeout: null }" x-cloak x-show="isVisible" class="" role="alert" x-on:pause-auto-dismiss.window="clearTimeout(timeout)" x-on:resume-auto-dismiss.window=" timeout = setTimeout(() => {(isVisible = false), removeNotification(notification.id) }, displayDuration)" x-init="$nextTick(() => { isVisible = true }), (timeout = setTimeout(() => { isVisible = false, removeNotification(notification.id)}, displayDuration))" x-transition:enter="transition duration-300 ease-out" x-transition:enter-end="translate-y-0" x-transition:enter-start="translate-y-8" x-transition:leave="transition duration-300 ease-in" x-transition:leave-end="-translate-x-24 opacity-0 md:translate-x-24" x-transition:leave-start="translate-x-0 opacity-100">
<div class="">
<!-- Icon -->
<div class="" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="" aria-hidden="true">
<path fill-rule="evenodd" d="M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0Zm-7-4a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM9 9a.75.75 0 0 0 0 1.5h.253a.25.25 0 0 1 .244.304l-.459 2.066A1.75 1.75 0 0 0 10.747 15H11a.75.75 0 0 0 0-1.5h-.253a.25.25 0 0 1-.244-.304l.459-2.066A1.75 1.75 0 0 0 9.253 9H9Z" clip-rule="evenodd" />
</svg>
</div>
<!-- Title & Message -->
<div class="">
<h3 x-cloak x-show="notification.title" class="" x-text="notification.title"></h3>
<p x-cloak x-show="notification.message" class="" x-text="notification.message"></p>
</div>
<!--Dismiss Button -->
<button type="button" class="" aria-label="dismiss notification" x-on:click="(isVisible = false), removeNotification(notification.id)">
<svg xmlns="http://www.w3.org/2000/svg viewBox="0 0 24 24 stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</template>
<!-- Success Notification -->
<template x-if="notification.variant === 'success'">
<div x-data="{ isVisible: false, timeout: null }" x-cloak x-show="isVisible" class="" role="alert" x-on:pause-auto-dismiss.window="clearTimeout(timeout)" x-on:resume-auto-dismiss.window=" timeout = setTimeout(() => {(isVisible = false), removeNotification(notification.id) }, displayDuration)" x-init="$nextTick(() => { isVisible = true }), (timeout = setTimeout(() => { isVisible = false, removeNotification(notification.id)}, displayDuration))" x-transition:enter="transition duration-300 ease-out" x-transition:enter-end="translate-y-0" x-transition:enter-start="translate-y-8" x-transition:leave="transition duration-300 ease-in" x-transition:leave-end="-translate-x-24 opacity-0 md:translate-x-24" x-transition:leave-start="translate-x-0 opacity-100">
<div class="">
<!-- Icon -->
<div class="" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm3.857-9.809a.75.75 0 0 0-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 1 0-1.06 1.061l2.5 2.5a.75.75 0 0 0 1.137-.089l4-5.5Z" clip-rule="evenodd" />
</svg>
</div>
<!-- Title & Message -->
<div class="">
<h3 x-cloak x-show="notification.title" class="" x-text="notification.title"></h3>
<p x-cloak x-show="notification.message" class="" x-text="notification.message"></p>
</div>
<!--Dismiss Button -->
<button type="button" class="" aria-label="dismiss notification" x-on:click="(isVisible = false), removeNotification(notification.id)">
<svg xmlns="http://www.w3.org/2000/svg viewBox="0 0 24 24 stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</template>
<!-- Warning Notification -->
<template x-if="notification.variant === 'warning'">
<div x-data="{ isVisible: false, timeout: null }" x-cloak x-show="isVisible" class="" role="alert" x-on:pause-auto-dismiss.window="clearTimeout(timeout)" x-on:resume-auto-dismiss.window=" timeout = setTimeout(() => {(isVisible = false), removeNotification(notification.id) }, displayDuration)" x-init="$nextTick(() => { isVisible = true }), (timeout = setTimeout(() => { isVisible = false, removeNotification(notification.id)}, displayDuration))" x-transition:enter="transition duration-300 ease-out" x-transition:enter-end="translate-y-0" x-transition:enter-start="translate-y-8" x-transition:leave="transition duration-300 ease-in" x-transition:leave-end="-translate-x-24 opacity-0 md:translate-x-24" x-transition:leave-start="translate-x-0 opacity-100">
<div class="">
<!-- Icon -->
<div class="" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="" aria-hidden="true">
<path fill-rule="evenodd" d="M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0Zm-8-5a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-1.5 0v-4.5A.75.75 0 0 1 10 5Zm0 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" clip-rule="evenodd" />
</svg>
</div>
<!-- Title & Message -->
<div class="">
<h3 x-cloak x-show="notification.title" class="" x-text="notification.title"></h3>
<p x-cloak x-show="notification.message" class="" x-text="notification.message"></p>
</div>
<!--Dismiss Button -->
<button type="button" class="" aria-label="dismiss notification" x-on:click="(isVisible = false), removeNotification(notification.id)">
<svg xmlns="http://www.w3.org/2000/svg viewBox="0 0 24 24 stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</template>
<!-- Danger Notification -->
<template x-if="notification.variant === 'danger'">
<div x-data="{ isVisible: false, timeout: null }" x-cloak x-show="isVisible" class="" role="alert" x-on:pause-auto-dismiss.window="clearTimeout(timeout)" x-on:resume-auto-dismiss.window=" timeout = setTimeout(() => {(isVisible = false), removeNotification(notification.id) }, displayDuration)" x-init="$nextTick(() => { isVisible = true }), (timeout = setTimeout(() => { isVisible = false, removeNotification(notification.id)}, displayDuration))" x-transition:enter="transition duration-300 ease-out" x-transition:enter-end="translate-y-0" x-transition:enter-start="translate-y-8" x-transition:leave="transition duration-300 ease-in" x-transition:leave-end="-translate-x-24 opacity-0 md:translate-x-24" x-transition:leave-start="translate-x-0 opacity-100">
<div class="">
<!-- Icon -->
<div class="" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="" aria-hidden="true">
<path fill-rule="evenodd" d="M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0Zm-8-5a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-1.5 0v-4.5A.75.75 0 0 1 10 5Zm0 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" clip-rule="evenodd" />
</svg>
</div>
<!-- Title & Message -->
<div class="">
<h3 x-cloak x-show="notification.title" class="" x-text="notification.title"></h3>
<p x-cloak x-show="notification.message" class="" x-text="notification.message"></p>
</div>
<!--Dismiss Button -->
<button type="button" class="" aria-label="dismiss notification" x-on:click="(isVisible = false), removeNotification(notification.id)">
<svg xmlns="http://www.w3.org/2000/svg viewBox="0 0 24 24 stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</template>
<!-- Message Notification -->
<template x-if="notification.variant === 'message'">
<div x-data="{ isVisible: false, timeout: null }" x-cloak x-show="isVisible" class="" role="alert" x-on:pause-auto-dismiss.window="clearTimeout(timeout)" x-on:resume-auto-dismiss.window="timeout = setTimeout(() => { isVisible = false, removeNotification(notification.id) }, displayDuration)" x-init="$nextTick(() => { isVisible = true }), (timeout = setTimeout(() => { isVisible = false, removeNotification(notification.id) }, displayDuration))" x-transition:enter="transition duration-300 ease-out" x-transition:enter-end="translate-y-0" x-transition:enter-start="translate-y-8" x-transition:leave="transition duration-300 ease-in" x-transition:leave-end="-translate-x-24 opacity-0 md:translate-x-24" x-transition:leave-start="translate-x-0 opacity-100">
<div class="">
<div class="">
<!-- Avatar -->
<img x-cloak x-show="notification.sender.avatar" class="" alt="avatar" aria-hidden="true" x-bind:src="notification.sender.avatar"/>
<div class="">
<!-- Title & Message -->
<h3 x-cloak x-show="notification.sender.name" class="" x-text="notification.sender.name"></h3>
<p x-cloak x-show="notification.message" class="" x-text="notification.message"></p>
<!-- Action Buttons -->
<div class="">
<button type="button" class="">Reply</button>
<button type="button" class="" x-on:click=" (isVisible = false), setTimeout(() => { removeNotification(notification.id) }, 400)">Dismiss</button>
</div>
</div>
</div>
<!-- Dismiss Button -->
<button type="button" class="" aria-label="dismiss notification" x-on:click="(isVisible = false), removeNotification(notification.id)">
<svg xmlns="http://www.w3.org/2000/svg viewBox="0 0 24 24 stroke="currentColor" fill="none" stroke-width="2" class="" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
</div>
</template>
</div>
</template>
</div>
</div>
@theme {
/* light theme */
--color-surface: var(--color-);
--color-surface-alt: var(--color-);
--color-on-surface: var(--color-);
--color-on-surface-strong: var(--color-);
--color-primary: var(--color-);
--color-on-primary: var(--color-);
--color-secondary: var(--color-);
--color-on-secondary: var(--color-);
--color-outline: ;
--color-outline-strong: var(--color-);
/* dark theme */
--color-surface-dark: var(--color-);
--color-surface-dark-alt: var(--color-);
--color-on-surface-dark: var(--color-);
--color-on-surface-dark-strong: var(--color-);
--color-primary-dark: var(--color-);
--color-on-primary-dark: var(--color-);
--color-secondary-dark: var(--color-);
--color-on-secondary-dark: var(--color-);
--color-outline-dark: var(--color-);
--color-outline-dark-strong: var(--color-);
/* shared colors */
--color-info: var(--color-);
--color-on-info: var(--color-);
--color-success: var(--color-);
--color-on-success: var(--color-);
--color-warning: var(--color-);
--color-on-warning: var(--color-);
--color-danger: var(--color-);
--color-on-danger: var(--color-);
/* border radius */
--radius-radius: var(--radius);
}
Data
List of all Alpine JS data used in this component.
Property | Description |
---|---|
notifications |
Array - List of all active notifications |
displayDuration |
Number - The amount of time, in milliseconds, the notification stays before disappearing
|
soundEffect |
Boolean - Activate/Deactivate the sound effect
|
addNotification()
param:variant param:sender param:title param:message |
Function - Renders the notification
|
removeNotification()
param:id |
Function - Removes the notification from the notifications array
|
isVisible |
Boolean - Notification message is visible/invisible
|
selectedOptions |
Array - Currently checked options
|
Keyboard Navigation
Key | Action |
---|---|
Tab |
Next focusable element gets the focus
|
Space Enter |
Focused element gets selected |