 Welcome to another episode of GUI Challenges, where I build interfaces my way and then I challenge you to do it your way because when our power is combined, we're going to find multiple ways to solve interfaces and expand the diversity of our skills. And today's GUI Challenge, we're building a loader. So I want to see what kind of loader is your building. But here, check out mine. Let's kick in that intro though. See this little bar that gets to go up and down and we can complete it and we can reset it. It's in an indeterminate state. We're building this and we built it, well I today built it on top of the progress element, which maybe was even regrettable. You know this location. This is so great. This is the debugging corner. I'm just going to hit refresh on all these browsers and we can watch my little script run that kind of just increments them over time. See how this thing is like going up a little bit and a little bit until it finishes. I'm just kind of simulating a loading experience because it's fun. And it's fun to watch. There we go. There's Safari. Anyway, let's do this. Let's go to V. Just hit save in our HTML file and watch them all. Yeah. That's satisfying. Do that work browsers. Load that level. Right. Okay. But I want to talk about a few like interesting things. Okay. See how as I focus here into the Chrome window, an outline shows up over our progress element. Same thing over here. Okay. Anyway, let's also just quickly say the loader that I built today is just the bar inside. For my demo purposes, you know, I have all these buttons. I have this like little card floaty thing it's inside of. That's not part of what I built. I built very specifically the loader inside of here and well, anyway, that's what we're going to break down. But still, look at it get a focus ring. Oh, look, the focus ring is gone because I clicked install focus and it's still showing over here in Chrome. If I click, it'll go away. But anyway, what what's happening here is like when I use my mouse to increase or decrease that after it sets the value on putting focus on the progress elements that it reads it out to screen readers, but I use my mouse. And so focus visible was the is kind of being smart here. It knows that a mouse causes the initial event that led to focus and it knows not to focus it. A mouse user doesn't need to see that focus. Now, if I tab into there with my keyboard and I hit spacebar, notice, I do see the outline, right? This is what focus visible does. It's like intelligent about which input type is doing the work. And even if it's being set from JavaScript, right? So I'm hitting spacebar, which is a click event JavaScript is setting focus on that progress element. I'm not calling focus visible or anything like that. It's just smart like them. But notice this, if I reload and a set timeout sets the value and the focus, it does show the outline. So focus visible is sort of like in an indeterminate state at this point, right? It's like, I don't know if my mouse or a keyword showed it. So defaults to showing the outline. So it's just an interesting thing is we're watching this demo today that that outline only shows up when we do this fun little auto load and set time out of setting it. So anyway, here it is working across all the browsers. You might be able to see it also some of the differences. Like we still have an animated bar here, which is really nice. I can still go to complete, but notice the complete state is missing the little checkmark that's over here on the one in Chrome. And Chrome, it allows me to animate this element where none of the other browsers do. So here, if I decrease and decrease, it's just an instant jump that you can, you can put a transition on there and it just doesn't do anything, which I mean, it's not a terrible, but it does mean that we kind of lose that little fun moment, but it is interesting though, right? That I was able to animate the indeterminate state. And that's just because I'm using a pseudo element at that point versus using the built in pseudo element, like shadow part that comes with the progress element. So let's check out the screen reader experience too. So here I'm going to go to Safari. I'm going to hit command F5 to turn on Mac OS voiceover voiceover on Safari progress for hit control. Just kind of skip a little introduction here, but it already has focus on the progress. And that was because earlier we set focus when the kind of complete event sent. So let's go down to the increase button and control again to kind of help it be a little bit more passive. I'm going to hit increase loading progress 100% how I hit increase. It was already full. Let's go to reset and decrease reset loading progress indeterminate progress indicator website main busy main load. Okay. Let's stop right there. It said the website was busy. It told us that the progress indicator was indeterminate and it told us that it was loading level and this is the loading progress, right? That's everything that we need to know if we can't see, which is really important. So I'm going to tab into increase, increase button, loading progress 20% progress indicator website main busy main. So when the increase button is pressed, I increment state, I update the progress element and I update it in a few different ways so that I know that the, um, screen reader users will be announced the changes and the changes will be represented visually for the actual element there. And that's one of the interesting choices here is that I chose to put focus on the element every update. But that's because I also don't have a demo here that's updating the progress element over and over and over again. So there's strategies for that too, which is during progress, you can put your focus onto a progress element. And if the user clicks or uses their spacebar, you can tell them what the state of the progress is. So it could be sitting there incrementing, incrementing and they're kind of just bored, curious and hits spacebar and they'll get to be told what the value is. So that's a interesting design choice I had in mind here and something that could be done better potentially in whatever scenario you put the loader into. So just something to be aware of, but I did think the announcement was really cool that it said the main was busy, that the website was busy and that's because we have an attribute on some, the HTML elements sort of like the loading zone here and, uh, we're creating this relationship between the progress element and a busy state. So that way as the page is doing something active and it is busy, screen reader users can know where and, and what, and then as soon as it completes, it says that it's not busy anymore and that's a good time to go update content and do all sorts of other things. But just kind of interesting that a progress element has all these different UX attributes from keyboard input to mouse input to being set by a timer, uh, and to being told to the user, how often do you want to tell them the state of the progress, like a visual user can see it in its entirety every update, but what is the experience that you want for somebody who's coming from a screen reader? So really cool stuff. Let's go check out some code next. Here is my HTML. I've got this main tag with an ID of loading zone and there's that aria busy equals true. So when this page loads and this particular case, the element is already busy, even though the progress here is unknown, there's a progress element showing indeterminate. So you, I felt like this was an active space. Something was going to happen. It was just unknown at its time of completion and there was no amount of progress yet. I also explicitly put the indeterminate attribute on here, but you don't have to, if you're missing a value at all, you are naturally indeterminate. So just kind of interesting. I just thought it was nice to spell it out. I also have roll progress bar on my progress element and that's for any browser that doesn't implicitly give the progress element, um, just the various aspects of what we expect from a progress. And so that will kind of marry that up to maybe more of an older browser. We are already described by loading zone. So this is part of the association that we can say this progress element is being described by, uh, this loading zone and there's a connection between their busy states and stuff. And we have tab index negative one. And this is how we've given the progress element and the ability to be focused from JavaScript. We're saying, Hey, programmatic focus is going to be available on this element, but otherwise, uh, don't let tab, um, focus go right into it. So that's another design choice that you could make, um, in your app and say, no, this is a focusable element and we're going to accept events on it, uh, et cetera. So anyway, that's pretty much the HTML, but notice something interesting. We have this label wrapping the progress and the label has a span called loading progress. Let's go back to our demo and this time let's look at it in Canary, uh, nice and large. Do you see that label? There's no label there, right? We see loading level and then a progress bar, but here in the code, we see loading level, loading progress. So this is a screen reader only, um, element that I've used CSS to hide from desktop users. While they're focusing this element, the progress can be better described. And so this says loading progress as opposed to loading level and you could hear that in the screen reader experience. They said loading level, loading progress, 20%. And I just thought that was really nice to have both those things there. Plus, I think it's important to label things. And so just having this label available for the progress, even if it's not shown all the time, it's still a valuable aspect in there. And then here inside of the progress, I put unknown, I do update this also with a string. So I update the, uh, value attribute. I update this string content and I update busy states and I set focus and I do a lot of things as values are changing. Um, but this is the initial loading state. And I thought that was kind of nice. Also, in case you're curious, here's all the buttons. So, you know, increase is calling an increase function decreases calling it. I could probably actually take off these IDs. Yeah, I don't even need those, but whatever. And we've typed some minutes, bleeding it and reset. So, um, that's just what was there. I thought that was mildly interesting to go over, but that's the extent of the HTML. Let's check out some of the CSS. Thanks to nesting, I can put everything inside of one selector here. Well, this year, let's look at these keyframes. What? There's just a 50% position left. That's powering that entire back and forth animation is at first it starts on the right. And then it goes to the left and then at a hundred percent, it goes back to the right. And that gives us a ping pong effect inside it anyway. I thought that was really neat. Let's just leave that expanded too. Okay, let's check out progress and go through some of the stuff in here. First, let's look at the custom properties. And this is how we're going to pivot the colors really easily, and it just kind of makes some of the, you know, long gradients like this one nice and easy to understand what they're for. So we have a track, we have a track size and we have a track progress. So the track is using HSL. We can see this is a pretty light color and this one is in the middle. So probably more vibrant. Yeah, both of them are high on saturation. So those are giving us a really good blues. Our track size here says, pick the smallest between one X and 10 pixels. And then we have this radius here, which is using scientific notation, which I just think it's fun because maybe if I wanted to have like, because this is like a thousand pixels right now, one with three zeros. But what if I wanted an even bigger number? I could just make this a five and then it goes up to, you know, one, one, two, three, four, five, which is a hundred thousand. We have a linear gradient going to right. And we want the track. So it's going to be the same color as the track all the way up to 45 percent. Then we're going to bring in a progress color, right? So here's our progress color. This is going to be that vibrant blue that we see and it's going to start right where this one ends. So that's the trick with zero here is it will naturally start with the last one left off. And so I could change, you know, something from 45 percent to 55 percent or 35, but maybe not 55. But anyway, it makes out really kind of dynamic and adjustable. So then we go from 45 percent to 55 percent. So the width of our ping pong indeterminate like little visual indicator is 10 percent. The width of the element that it's inside of. And then we end it with starting at the end of this one. We go to the same track color. So we're essentially like invisible up until our progress, which is 10 percent wide. And then we're invisible again. We're perceptively invisible. And then we animate that. So then here's our track size, which is another kind of crucial aspect to this, which is the width we're setting to 225 percent. And that I thought made it so that that middle piece bounced really well inside of the container. We saw it go from edge to edge in like a nice smooth way and the height is just 100 percent. And here's the animation, which we just saw progress loading two seconds, infinite ease. So it's going to call those keyframes, which is going to change the linear gradients position to left to right to left to right. So kind of cool. I hope I explain that well. We'll see here. We're resetting some of the styles. So we're setting it to appearance notes. We are using the progress element. We're stripping a lot of the styles that are there. So there's an easy way to colorize the progress element and to get an adaptive one. And that's using color scheme and accent color, which we've covered a lot in this show. And I have some nice blog posts on. So we'll link those here in the show notes. But anyway, we're getting rid of all that. And we're saying, no, we're going to take over and we bring in some custom style. We're going to set position relative because we're going to have a pseudo element. We're setting the height to that track size variable that we set earlier and the width or done at the width. But the border radius to something really large, which is sort of guarantees its round. So in case you wanted to make this loader bar really tall, this guarantees that you're going to have a rounded corner in all those different scenarios. And we set overflow to hidden so that border radius passes down to all the children and becomes a nice clipping area for our animation that we have with the ping pong background linear gradient. Here's where we go adaptive. We set we're nesting here, which is really nice for saying if it's dark color scheme preference, we're going to set the track color to something much darker here. So we dropped the lightness and we dropped the saturation. Our progress is going to say very bright, very vibrant. And we're going to increase the brightness there. So that's just kind of a nice thing to do as you kind of take away a lot of the surface area and a lot of the brightness, but you can still have vibrant like candy colored looking colors in a dark mode. So anyway, here's our focus visible that we talked about earlier. We're giving it the outline color that matches our progress, kind of cool. And here's kind of, I think some of the most valuable aspects of this entire kind of blog post and study is finding these kind of obscure selectors and showing you that you can't group them together. This is just something that you'll naturally want to do is use a comma here and try to drop Moz progress bar with WebKit progress bar. And that is not going to work. Each one is for each browser and they don't have tolerance enough to let one exist and the other one not exist. So it basically fails the whole thing if you show a Moz progress bar selector to WebKit, for example, or to Chromium. So you have to break them up and they all have these kind of funny names, but I've given them some comments here. So I think you should write these down or link the blog post because this stuff is hard to find these days. And anyway, also, look, I have here it starts with the selector and values. So we have the progress element is the context. We're making sure that there's an actual value here so that we're not in an indeterminate state. It's kind of what it means. And then we have this WebKit progress bar selector. We're setting the background color. So this is the track progress bar. Then we have the progress value, which we had as our progress color here, and that's we're setting the background color of. And on WebKit, we're allowed to transition that. So Safari is getting this transition. It just doesn't apply it. Next, we have Firefox and we're targeting its Moz progress bar. It doesn't have a progress value, so we're only allowed to change the background color. And then we have indeterminate here. So notice we have three different ones. Custom properties is helping us a lot and I not have duplication, but some of it we can't really get away from. But here's indeterminate for all of them, which creates the sudo element. It's content is none, so that's how you make sure you get the node. It's inset is zero, so that's going to make it fit all the corners of the space. It's kind of like setting top right, bottom left to zero all at the same time. We have position absolutes so that those go together. Then we set the background color, background size, the background position to right. Remember we were talking about that earlier. And then we apply the animation, which is going to make it go from left to right to left to right to left to right forever. And it's Safari. We're going to target the WebKit progress bar when it's indeterminate and give it these properties. Indeterminate Firefox is going to get these properties. And there's a specialty one I threw in here, which is a complete. So if there's no maximum value, and the value is one because the maximum default of a progress element is one. So it's like using decimal places to show the progress. Point one instead of 10 out of 100, it's point one. Anyway, but I also threw this one as in there as like an added bonus. Like if max is a hundred and values a hundred, we know that this thing is complete. And I use a before pseudo element this time. And I put a check mark on there. So some browsers allowed you to do this and some didn't. And that was just, that was just this case with this element. Some things are not allowed. And we again set the position absolute. We this time use the inset logical property. So we have inset block to zero. So that's going to do the top and the bottom to zero and inset inline to auto and zero. And that's going to push it to the right. Anyway, it's just kind of interesting. We have display flex in the line down center. That's going to make sure our checkbox is in the center, padding in line and so again, since we used logical properties here and we used a progress element in case the directionality of this page changes from right to left, our checkbox will be on the correct side with the element. And so we're just sort of staying within relative flow. And that's what logical properties let us do and think less about directionality. We're just sort of tapping into whatever the document direction is by using logical properties. You know, I'm a big fan of those. I kind of want to reach more and write less and logical properties is a great way to get there. We set the color to white for this checkmark and we give it a font size. So that was it for the whole progress element, but I thought these selectors were really interesting. Putting the pseudo element on there. And if it didn't allow a pseudo element on the progress element, then we styled the progress bar and this progress bar. And it's annoying having to duplication, but custom properties make it pretty fine. Anyway, and that's it for the style. So the last thing we have to go over here is the JavaScript. Let's go check that out. JavaScript over here. Okay, at the top, we've got two selectors. These grabbed the progress element and the loading zone because we need to communicate with those as progress is happening. We set some initial state. Definitely better ways to set state, but whatever. We have this function called around decimals, but this is really because I was working within JavaScript's decimal systems. And like if you do 0.2 plus 0.1, maybe you know this, it equals, I don't even remember. It's like 2.999998 or seven or something like that. It's like, wait a sec, JavaScript can't add decimals sometimes and it is true. And so what we're doing here is kind of correcting that behavior so that I can add in these various functions down below, just my simulation functions, they just want to increment by 20%. So they add by point anyway, whatever you shouldn't need that. But here, set progress. This is where all the good stuff is at. At first, whenever progress is being set, we set the aria busy state to whether or not the state.value is less than one. So if it's at one, it's complete. If it's less than one, it's not complete. So busy is going to be true whenever it's not one. Kind of cool, but that's all it took to set the busy state of the zone. Then we clear the attributes if no value to show and the progress element will show indeterminate at that point. So we remove the aria value now, which we populate as well as value. And that's something that makes sure it gets read to screen readers. And then we cause focus again cause, cause we emptied it out and now it's indeterminate. The new status needs to be told to the user. Then we round the bad decimal math right here. So we take the value and we make sure it's something that we can present. We take the value percent. So we take value times 100 and add a percentage here. And that's what we use as the text inside here. Look, here's valve percent and we use that as the aria now. So that way when it gets read to someone, it gets read as a percentage. So it's not like a progress element is at 0.6. No, I'd say no progress element, 60%. Right, we wanted to read something to somebody that's nice and user friendly. So that's some good UX tips right here. We set the value, we set the aria value now and we set the intertext. And last we call focus again. And that is the extent of the JavaScript. I mean, here's some of my like set timeouts that do that page simulation stuff. And here's my functions for the events, but for the demo. But that's the extent of the JavaScript. We make sure that the aria busy state is set. We manage our attributes, whether or not we're complete or not complete. And we make sure that we populate the value, the aria value now and the intertext. And that's just important for us to reach as many people as we can with this new value. And lastly, we set focus just for screen readers so that they can hear that new value. Awesome, thanks for hanging in there for this GUI challenge. I'm really excited to see what you have created with progress elements. I've seen progress elements in just pseudo elements and all sorts of cool stuff. And I think if I was to do this again, I would build my own. The JavaScript that I've written and the amount of attributes that I can put on something, I do feel there's an opportunity to make a better animated cross browser loader experience if you don't use the built in progress element. So that's kind of unfortunate, but that might just be the scenario that we're at. Anyway, I'm looking forward to those submissions. Thanks again for hanging out and I'll see y'all later on the next GUI challenge.