 Welcome to another episode of GUI Challenges, where I build interfaces my way. Then I challenge you to do it your way. Because with our creative minds combined, we'll find multiple ways to solve these interfaces and expand the diversity of our skills. Yolukis, hit in that intro. Thank you for that robo-intro. Today's episode is all about toasts. Here we are. Oh, well, here let's cast more spells. Ice 4, Ice 3, Dark 5. These are just fun. That's like just to show that, you know, you're going to want to cast more than one spell if you're casting spells. And so toasts better be able to handle that. You're going to cast spells until you run out of magic points, right? I mean, that's what I do. But here, here's some more realistic toasts. Oh, well, that's not one. Multiline Support is just a proof of, like, value. But look, they were saved. Okay, yeah, that's nice. Added to favorites. I've definitely seen that toast before. Added to cart. That's nice. This is good stuff to just sort of like passively tell the user a little bit of feedback coming from the system that you've built. Let's test out the light theme. You know, of course it handles the same stuff. It's a little bit transparent, as you can see there. Notice that it doesn't get interrupted here, so I can't click them. I'm using pointer events none to do that. I'm using that on the container. Did you see where the container was? Hey, here's body. What is this doing up here above the body? What is that? Oh, we'll talk about that here soon. Let's first reduce the motion. Got some reduced motion and we fade in and we fade out. We fade in and we fade out. Also, if we look in the console, we can see that we're getting an event. Well, we're console logging once it's all done. So here, if I cast a spell lit for, it goes away and we console log. So that's because we're returning a promise from the toast and we can await that promise for when it resolves and know that a toast has shown and go away. Right? Super cool. Let's go back to motion. Yeah, we want some motion. Let's go back to the dark mode. Cool. Okay. I want to show you a couple more things. If we come into the elements panel, like we were looking up before, hit escape to close that console. Let's cast a bunch of spells. Go to the animations panel and pause them. Now we can go back to the elements panel and see all of these without them disappearing. Isn't that just neat? And look at what I have chosen here. I've chosen the output element. The output element I learned from user feedback and previous GUI challenges that this will be amounts to screen readers by default. And furthermore, if you add role status onto it, you ensure that older browsers that don't implicitly give an output the role status will put role status on this element anyway and announce it to users. So you kind of get a win-win old and new browsers all with one element. So now whenever you throw an output element onto the page, it will be announced to the user. Also, if you change the value of one, it'll be announced to the user as well. So that was the way that I chose to choose my semantic markup, interact with screen readers was all through this one element. And let's see what else can we talk about in here? And while we have it frozen, let's look at our layout. So we have this GUI toast group. It's position fixed. So you can see it's pinned down to the bottom of the viewport. We, again, are above the body. So we only need to use Zindex one. We shouldn't really be competing with other elements. Our only other sibling to compete against is the body tag. And are you putting Zindex really big on that? I don't know. Maybe you are at which point it would compete. But anyway, kind of nice. It's in this like safety zone where it's just above the body. And it's in a really nice page-level location. Don't have to battle with a whole bunch of Zindex. We pin it to the bottom with inset block end. We fill the width with inset inline. So it pins the left and the right to zeros. Give it some padding on the bottom there. So that's why all of them are padded the same way as they enter into the viewport. And then we set justify items and justify content center. So if I take out justify items, you can see that the content is centered, but each item isn't individually. They're stretching to fit. Okay, go ahead and stretch to, or, you know, bring your intrinsic size here. And let's just align into the center and justify content. Yeah, it puts the content in the center there. We have gap because that's just nice to have between all of our toasts. And we're doing all of that layout positioning with grid. And then as each toast comes in, let's look at a toast. There's just a regular font family set on it. A background color and a text color. This is what we flip in the dark and the light theme so that we have it a dark and a light version, pretty simple version. And then we have an online size using a calculation here. So it never goes above 25 characters or never above 90% of the viewport with. And that's kind of nice is now that even like a tiny mobile, if it's using a really wide toast, you're not going to have a bad layout. We give it a little bit of padding on blocks. So that's like top and bottom padding in line like left and right border radius at three pixels. So just a subtle little curve on there as you can see border font size of one rim. And then we have an animation that specifies three animations in a comma separated list. So we're going to fade in over 0.3 seconds, slide in over 0.3 seconds and a fade out that waits a duration. And the duration is three seconds. And so that's why we fade in and slide in right away. And then we wait three seconds and then we fade out. So that entire toast life cycles handled on this toast element itself. And the gooey toast group is the one that slides up and down as we make room for other elements. So we'll go ahead and play that out and let those go. Right. So here's the slide in. Let's play and pause. Well, I wanted to grab the handle, but anyway, we can see that that's the animation handling. Oh, that's not really what I want. Is it? So like let's try here. There's a handle I want. Oh, I can't see the animation play, but that's okay. You can see that our timeline here shows the fade in slide in a wait of three seconds and then a play of fade out. This particular animation on section gooey toast is coming from web animations API. And that's what slides up the whole container. And it gives us that cool little look as we do this. Kind of neat. So let's dive into some of the JavaScript that powers this and a little bit more of the CSS. In my code editor, this is the HTML. We've got two buttons and that's it. And notice there's no element here between the head and the body yet. That's going to get inserted from JavaScript when JavaScript imports toast from toast. And we have our spells button here looking for clicks, casting a random action. Well, here's our console log poof. So look, we're waiting for a click. We have an asynchronous callback function that can then await the promise returned from a toast and console log when it's all done. Super nice little setup. And here's one that just sends them all asynchronously and doesn't do anything about the weight. These are just the random action and a couple assistive functions down here to make that sort of random as we're pushing buttons. Up here in the styles, here's our gooey toast group, our gooey toast group. Because there's some cool tricks in here. So we saw that the duration was being used to save how long the toast was being shown. BG lightness is how we're flipping the values of the background colors. We're just toggling the lightness in here. It's kind of nice. And travel distance. This one's really fun. The travel distance is initially set to zero. We're assuming that motion is not okay. And if motion is okay, we allow it to travel a distance. Called slide in. But maybe it doesn't go anywhere. If there is no travel, if travel distance is set to zero, which is the default, it won't slide up at all. We set a value here of 10 pixels in case like this property didn't get set, but anyway, that's not really super important. What's important here is that the keyframes have no idea whether or not motion is on or not. They don't really care. What we're going to do is we're going to toggle how far it moves. And if it's okay to move stuff, it's not actually going to be okay. So we're going to do that. And then we're going to do it in a way that nothing really knows that it's being toggled. And I thought that was kind of nice. Here's our animation that we're looking at earlier, fade in, slide in and fade out. And here's the keyframes. Fade in from opacity zero. Fade out to opacity zero. And slide in from translate y. So maybe slide in from your modulated slide in that way. So I thought that was neat. I wanted to share those pieces. But essentially, the rest of this conversation needs to be in the JavaScript. At a high level, we have an initialization function, which is going to run when the module is used. We have a create toast function and add toast function, the main toast function that we export here as default, and then a function called flip toast, which is not like flipping toast, you know, like on a griddle or something like that. So we have a text invert play and we'll dive into that. I'm excited about that. All right, let's just look here at a high level. We have our main function that we're exporting. So what happens when somebody like over here calls toast and they put a random spell string in there. That string comes in as text here. We create a toast. So let's go look at create toast. Creates an element called an output, gives it the text, adds a class and sets the attribute role status, returns the element. Okay. So here we have a variable. We add that toast. Let's look at add toast. Checks to see if motion is okay. So we're looking at the user preference. Then we say here toaster.children.length. So is this greater than zero? Is there children in the container? And is motion okay? If that's cool, we're going to flip that toast. Otherwise just append that toast to the group and that's all good. And we'll talk a little bit more about this, but essentially this is going to be where we're choosing to animate. If it's okay, the appearance of the grid, you know, making room for the new toast in there. Anyway, we'll get into that. And that's all handled here in add toast. And next is we return a new promise from this toast function. And we're going to have an asynchronous callback where we're going to get two of the promise functions given to us, which is resolve and reject. And we're going to call resolve here at the very end. But what we're going to do next is, so we have an asynchronous function, we're going to await the animations on this toast, right? We have three keyframe animations fade in, fade out, and slide in. Once those have all completed, we want to know. So we're going to say promise. All settled, which takes an array of promises. We're going to map each of the animations on a toast. Here's each animation. We're going to return the finished promise. So this essentially returns an array of three finished promises given for the three keyframe animations in there. And we're awaiting that all settled. So when all three are done and resolved, we can safely move on to the next line of code here where we remove the child toast from the DOM. And then we resolve the promise, at which point we come back into our code over here and we can console log poof because we awaited the promise that came from that. That was pretty intense. That's a lot of the bread and butter of what's going on here. But let's talk next about flip toast because that was a really fun feature of this toast system here. So hopefully, you know who Paul Lewis is. He's been a very famous front end developer at Google for a long time. And he came up with this flip technique where you can essentially animate things from an old position to a new position in a scenario where it's very dynamic. So right now we're adding toasts. Let's go back to our code really quick. We're adding toasts and they can be multi-line. Thank you. That was really aptly timed. Now, that means we can't assume we know the height of these, which also means we can't just animate the height of something and we don't have height 100 animations. And so what we really need to do is slide up the entire group from the original position. It's like here. Let me just actually pause. Let's see if I can just pause these and try to project a little bit what's happening here. So here's the first position, right? Let's go back to the code as well. When the flip toast function is called, it's given a toast element and it goes and grabs the current position of the toaster. And let's go here. That would be this group. So notice its height and its position and it's got a position globally in the page. And we go ask for that this offset height. Then we add the element into the container, which is going to make the grid resize and instantly snap to a new location. At which point we go ask for its offset height again, the last position. So this is its new position. This is its old position. What we do here is we find the delta. So we look for the invert. We take the last minus the first. What's the difference between its old location and the new location? And we create an animation from the old one to the new one, which is just zero. It's just in its current place that it's now at, right? We set it here. We gave it a new layout. We've measured and now we're going to create an animation in that same frame that takes it from an old position to a new position over a duration of 150 seconds, milliseconds and ease out. And we set the start time equal to document timeline that current time. This fixes a Firefox bug. But what this specifically does is this tells the animation to begin immediately. Don't wait for the next frame. And that's important because we're kind of playing a little game here with our first last invert. These things offset height causes a reflow. So this is a kind of expensive property to ask for. And the browser has to do a little bit of work to get there. And you pay a price, which is like a frame, it has to draw the page. And we append the child, which is going to draw. Well, this also draws. So we just caused like three draws. And then we're going to do some math and cause an animation that we don't want to wait a frame. We want it to happen right away. So we set the start time to the current timeline in the document. And that is how flip works. So essentially we take, oh, let's go back to our example here. Here's our first position. Let's sort of play through or add another one. And then we have a new position here. And what we did is I wonder if these animations will even show us. I don't think so really. We faked it. So we immediately put the element into the new position and then scooted it back and slid it back into its new spot. It's very tricky in terms of like the illusion that it creates, but it is not quite too much to handle over here. I thought this was pretty simple code to follow. And we got a really cool effect. So that allowed us to dynamically slide from the old layout of grid to a new layout. And you know, right? Grid is managing the gap. Grid is doing all the hard work of the layout. And we don't have to do any of that. All we need to do is tie into its sort of life cycle and make sure it has the appearance of sliding up and back out. And I thought that was kind of cool. So that's how that is handled. And that's why it also respects reduced motion and all that sort of good stuff. Have fun casting spells. Have fun making your own toasts. I hope you enjoyed this episode of Gooey Challenges. And I'll see you later, y'all.