On this page
Toasts
A lightweight status message in the top layer, using
popover="manual" and
command / commandfor. Optional
<pe-toast> adds data-delay and stacking.
No Toast plugin.
| What runs it | JavaScript | |
|---|---|---|
| Bootstrap 5.3 | Toast plugin | 5.4 KB gzip |
| This repo | popover=manual. Optional pe-toast for data-delay and stacking. | 0 KB. Optional behavior for autohide. |
| Optional | <pe-toast> · src/behaviors/toast.js . data-delay autohide and corner stacking. Show and close work without it. |
|---|---|
| Avoid |
|
How it works
-
popover="manual"puts the toast in the top layer and keeps it open until something hides it. There is no light-dismiss. -
command="show-popover"andcommand="hide-popover"show and close it. That needs no extra script. -
:popover-openreplaces.show. The container’stop-0,bottom-0,start-0, andend-0choose the corner.
Live example
A status toast. Show opens it. Close hides it. It stays until you close it.
<button type="button" class="btn btn-primary" command="show-popover" commandfor="liveToast">
Show live toast
</button>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div class="toast" id="liveToast" popover="manual" role="status" aria-live="polite" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto">Bootstrap</strong>
<small>11 mins ago</small>
<button type="button" class="btn-close" command="hide-popover" commandfor="liveToast" aria-label="Close"></button>
</div>
<div class="toast-body">
Hello, world! This is a toast message.
</div>
</div>
</div> Custom content
A toast can carry actions. Those buttons hide the same popover.
<button type="button" class="btn btn-primary" command="show-popover" commandfor="customToast">Show custom toast</button>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div class="toast" id="customToast" popover="manual" role="status" aria-live="polite" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto">Bootstrap</strong>
<button type="button" class="btn-close" command="hide-popover" commandfor="customToast" aria-label="Close"></button>
</div>
<div class="toast-body">
Hello, world! This is a toast message.
<div class="mt-2 pt-2 border-top">
<button type="button" class="btn btn-primary btn-sm" command="hide-popover" commandfor="customToast">Take action</button>
<button type="button" class="btn btn-secondary btn-sm" command="hide-popover" commandfor="customToast">Close</button>
</div>
</div>
</div>
</div> Color schemes
An important toast uses role=alert and aria-live=assertive. Color utilities sit on the toast.
<button type="button" class="btn btn-primary" command="show-popover" commandfor="colorToast">Show color toast</button>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div class="toast align-items-center text-bg-primary border-0" id="colorToast" popover="manual" role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body">Hello, world! This is a toast message.</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" command="hide-popover" commandfor="colorToast" aria-label="Close"></button>
</div>
</div>
</div> Placement
top-0 start-0 and bottom-0 end-0 on the container place the open toast.
<div class="d-flex flex-wrap gap-2">
<button type="button" class="btn btn-primary" command="show-popover" commandfor="toastTopStart">Top start</button>
<button type="button" class="btn btn-primary" command="show-popover" commandfor="toastBottomEnd">Bottom end</button>
</div>
<div class="toast-container position-fixed top-0 start-0 p-3">
<div class="toast" id="toastTopStart" popover="manual" role="status" aria-live="polite" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto">Bootstrap</strong>
<button type="button" class="btn-close" command="hide-popover" commandfor="toastTopStart" aria-label="Close"></button>
</div>
<div class="toast-body">Hello, world! This is a toast message.</div>
</div>
</div>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<div class="toast" id="toastBottomEnd" popover="manual" role="status" aria-live="polite" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto">Bootstrap</strong>
<button type="button" class="btn-close" command="hide-popover" commandfor="toastBottomEnd" aria-label="Close"></button>
</div>
<div class="toast-body">Hello, world! This is a toast message.</div>
</div>
</div> Accessibility
Use role="status" with aria-live="polite" and aria-atomic="true" for an ordinary message. Use role="alert" with aria-live="assertive" for an important one. The toast does not take focus.
popover="manual" does not light-dismiss. Hover and focus pause optional data-delay autohide so a reader can finish the message.
Browser support
| No script | Polyfill | No fallback |
|---|---|---|
popover="manual" with command to show and close. No light-dismiss. | None for show and close. Optional pe-toast for data-delay and stacking. | Without pe-toast, open toasts share one corner. |
Autohide and stacking
<pe-toast> is the toast itself, with
class="toast". Load
src/behaviors/toast.js when you want it.
data-delay is milliseconds. Omit it and the toast stays
open. Hovering or focusing the toast pauses the timer and starts a
full delay again when the pointer and focus have both left. Open
siblings in the same .toast-container are offset so
they stack. Without the module, one toast still sits in the corner,
and a second one shares that corner.
Stacking is the one place this package measures an element. The
behavior sets --pe-toast-index, the number of open toasts
before this one, and --pe-toast-stack, the sum of their
heights. CSS adds one --bs-toast-spacing per toast, so
the gap is a token and not a number from JavaScript. Toasts in the top
layer do not share a layout box, so a container cannot space them the
way .toast-container spaces in-flow children.
When every toast in a corner is the same height, you can drop the
measurement. Percentages in translate resolve against the
element’s own size, so the index alone places the stack and
--pe-toast-stack goes unused. The element still supplies
the index, because only open toasts count toward it:
.toast[popover] {
bottom: 1rem;
translate: 0 calc(var(--pe-toast-index, 0) * -1 * (100% + var(--bs-toast-spacing)));
} Stacking
Both toasts hide after 5 seconds unless the pointer or focus is inside them. Show both to see the stack.
<script type="module" src="./node_modules/bootstrap-html/src/behaviors/toast.js"></script>
<div class="d-flex flex-wrap gap-2">
<button type="button" class="btn btn-primary" command="show-popover" commandfor="stackToastOne">Show first</button>
<button type="button" class="btn btn-secondary" command="show-popover" commandfor="stackToastTwo">Show second</button>
</div>
<div class="toast-container position-fixed bottom-0 end-0 p-3">
<pe-toast class="toast" id="stackToastOne" popover="manual" data-delay="5000" role="status" aria-live="polite" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto">Bootstrap</strong>
<small>just now</small>
<button type="button" class="btn-close" command="hide-popover" commandfor="stackToastOne" aria-label="Close"></button>
</div>
<div class="toast-body">See? Just like this.</div>
</pe-toast>
<pe-toast class="toast" id="stackToastTwo" popover="manual" data-delay="5000" role="status" aria-live="polite" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto">Bootstrap</strong>
<small>2 seconds ago</small>
<button type="button" class="btn-close" command="hide-popover" commandfor="stackToastTwo" aria-label="Close"></button>
</div>
<div class="toast-body">Heads up, toasts will stack automatically.</div>
</pe-toast>
</div>