<button id="spells">
Cast Spell
</button>
<button id="actions">
Mock User Action
</button>
. gui-toast-group {
position : fixed ;
z-index : 1 ;
inset-block-end : 0 ;
inset-inline : 0 ;
padding-block-end : 5 vh ;
display : grid ;
justify-items : center ;
justify-content : center ;
gap : 1 vh ;
/* optimizations */
pointer-events : none ;
}
. gui-toast {
--_duration : 3 s ;
--_bg-lightness : 90 % ;
--_travel-distance : 0 ;
font-family : system-ui , sans-serif ;
color : black ;
background : hsl ( 0 0 % var ( -- _bg -lightness ) / 90 % );
max-inline-size : min ( 25 ch , 90 vw );
padding-block : .5 ch ;
padding-inline : 1 ch ;
border-radius : 3 px ;
font-size : 1 rem ;
will-change : transform ;
animation :
fade-in .3 s ease ,
slide-in .3 s ease ,
fade-out .3 s ease var ( -- _duration );
@media (--dark) {
color : white ;
--_bg-lightness : 20 % ;
}
@ media ( --motionOK ) {
--_travel-distance : 5vh ;
}
}
@ keyframes fade-in {
from { opacity : 0 }
}
@ keyframes fade-out {
to { opacity : 0 }
}
@ keyframes slide-in {
from { transform : translateY ( var ( -- _travel -distance , 10 px )) }
}
const init = () => {
const node = document . createElement ( 'section' )
node . classList . add ( 'gui-toast-group' )
document . firstElementChild . insertBefore ( node , document . body )
return node
}
const createToast = text => {
const node = document . createElement ( 'output' )
node . innerText = text
node . classList . add ( 'gui-toast' )
node . setAttribute ( 'role' , 'status' )
node . setAttribute ( 'aria-live' , 'polite' )
return node
}
const addToast = toast => {
const { matches : motionOK } = window . matchMedia (
'(prefers-reduced-motion: no-preference)'
)
Toaster . children . length && motionOK
? flipToast ( toast )
: Toaster . appendChild ( toast )
}
const Toast = text => {
let toast = createToast ( text )
addToast ( toast )
return new Promise ( async ( resolve , reject ) => {
await Promise . allSettled (
toast . getAnimations (). map ( animation =>
animation . finished
)
)
Toaster . removeChild ( toast )
resolve ()
})
}
// https://aerotwist.com/blog/flip-your-animations/
const flipToast = toast => {
// FIRST
const first = Toaster . offsetHeight
// add new child to change container size
Toaster . appendChild ( toast )
// LAST
const last = Toaster . offsetHeight
// INVERT
const invert = last - first
// PLAY
const animation = Toaster . animate ([
{ transform : `translateY( ${ invert } px)` },
{ transform : 'translateY(0)' }
], {
duration : 150 ,
easing : 'ease-out' ,
})
animation . startTime = document . timeline . currentTime
}
const Toaster = init ()
export default Toast
Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License , and code samples are licensed under the Apache 2.0 License . For details, see the Google Developers Site Policies . Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2023-07-05 UTC.
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2023-07-05 UTC."],[],[]]