 Welcome to GUI challenges where I create interfaces my way and then I challenge you to do your way. And with our creative minds combined, we're going to find multiple ways to solve these interfaces and expand the diversity of our skills. And in today's challenge, we're building stories which is a full-screen tap-centric user experience for quickly or casually navigating a multi-dimensional array of content. And I've got it over here. I'm going to demonstrate it across multiple browsers. Here I am inside of Opera. Here I am inside of Chrome. And so the way that this works in case you don't know is if you click on the left-hand side of an image, you go back through the stories or back through your users and your friends. And if you go on the right, you go through your users and through their stories. There's also keyboard navigation built into mine. And since I used a couple of interesting techniques here, I can swipe or I can use my finger to swipe and scroll through multiple. I even look here on Android. So if I tap on the right, I'm going through my friends. I can also swipe through my friends and go directly to their stories, which is kind of cool, right? All right. Here I am in iOS, swiping through, swiping through, tapping through. It's all gravy. Right. Okay. So now that we know it's working across all these browsers and it looks super rad, let's talk a little bit about how it's made and what sort of tools I chose. So let's start with our markup. And this is the semantic markup that I thought was really neat here is we have a stories component. So this is just our custom class name called stories. This is what we're going to use as a base. And inside of here, we want to have some sections in our sections are going to be our users. And we want to simulate three users just to help us pre populate this a little bit. And then inside of each user, each story is going to be an article. And we want to do that times three as well. So good old Emmett, if I hit tab, that expands into some markup. And I just love that feature and wanted to show you that you can sort of think about your semantics in the way that you're defining them. And with that starter markup, I just went in and added a inline style here with a custom property called BG. And we're going to use this URL in the background image of our story of our article here. And we're going to use that image in a multi layered background image so that we get some loading states for free. I'll show you that cool trick in just a second here. But I just loved the stories, sections, articles, it seemed to make a lot of sense to me. And I wanted to call that out. And yeah, if you're wondering how the random images come in, you can see there's just slightly different variations into the photos here. So super cool. And what that looks like up here in the dev tools, I can scroll over, we've got our stories here. Each story has some sections and you can see here, I'll just pan over so we can see how that goes off into the left. We've got columns of content here. And inside of each section, we have their stories and this particular user only has one. I think this user only has one also I wanted to simulate something slightly real. And if we come over here, this user should have a few. Yeah, but look, they're stacked on top of each other. How did I do that? We'll get there. If you haven't noticed yet so far, we're using grid and scroll snap points, but we're going to talk about that in the CSS section. But that essentially concludes like our markup. I thought that was interesting. So let's jump into our CSS. Let's talk about stories and how these stories are getting made. And here we are looking at display grid display grid, just one of my favorites. And this is some shorthand that I want to expand for you. It's super sweet. I love this. So what we're saying here is our first item in the shorthand is our rows. We're doing rows slash columns. And we want our rows to always be one FR and we'll go look at the dev tools here in a second. But if we're on mobile, one FR is going to be the full viewport. And if we're on desktop, one FR is going to be whatever I've determined is like a good breakpoint. That's why we have these demos over here that look really nice is because I've maximized that. But what's nice is my stories, they don't really know or care the size of what they're inside of. They're just going to fill it. And our columns are where it gets kind of interesting here. We are saying we want to autoflow our columns. So we're not going to set template. What we're going to do is we're going to say we want you to autofit any number of children that you find need to autofit into this flow. So I want you to flow from left to right in column order. And each column should be 100 viewports wide. So this is a grid auto flow. This is grid auto columns. So this is grid template column. This is grid auto columns. And together with a little bit of a gap, we get this layout right here. We get that whole layout of all of these sections for free, ready to be overflow scrolled and scroll snap pointed. So it's just really nice. And look, what's next? Oh, it's overflow x auto. That makes a lot of sense. We were just defining a grid that should bust out of its own viewport. And by specifying overflow x auto, we get a scroll bar. And when we add scroll snap point mandatory x here, we're saying we want all of our children to scroll on x and stop on each item as we can. We want you to find somewhere healthy to stop inside of our children. Go though, ask our children what they want. They're going to tell you. So it's the parent child relationship between stories and users is the way that the scroll snapping works. So anyway, here's the parent describing what it wants. And I like this little feature here too. This is over scroll behavior x contain. What that means is when someone is panning or using their mouse wheel to scroll inside of a space, the wheel event won't leak outside of the container. And let me just demonstrate that really quick for you inside of our demo space. So again, here I have all the browsers. And if I reload on Chrome, and I hit next. So our goal here is basically to overwhelm the school event. So first one here, I'm panning left, panning left. And let me just refresh over here in Safari. And I'm a pan left really fast. You can see how my event is sort of leaking out of the container. You can even see it leak out of the container. If I go on the right here with Chrome, I go all the way the right and I'm still swiping right and I don't see a bounce. If I do that over here, I will see a bounce. So that's what overflow scroll contain is doing. It's really nice for modals and other scenarios where something might be scrolling inside of another scrolling view. And you're making sure this traps it. And this is nice for the stories here because we do have the page is scrollable. See how I'm bouncing the page here with my mouse. But if I do that inside of here, I'm trapping it on X. I said over scroll behavior X contain. Now, vertical is still overflowing, but that's not what I told it to do. So anyway, I really like that feature because that means no one will accidentally scroll into the parent context and lose what they were doing because they were trying to scroll fast. Anyway, that's one of my favorite tips. So if that's stories where we defined a grid that was columns, right? So we'll come back in here stories, grids and columns. Super cool. How about each user? Each user needs to go define how it's going to be displayed and what its children should be. So let's pop it open. Here's our outer registration. So I like to separate these out sometimes, especially when they're quite distinct. And in this one, our outer registration here saying we want to scroll snap a line start. So our parent defined us as a scroll snap container. We are a child of that. And here's where we want to snap. We want all of these users to snap to the start, which is essentially the left edge, which gives them a very centered full screen, you know, feel. Anyway, this one is sort of new though. This is scroll snap stop always. And what I want this to do is I want this to never let somebody power scroll through a bunch of users. I want this to force them like a carousel one by one. I'll demonstrate that over here on Android and iOS. So we have Android that as I try to pan over multiples and throw, I can't really throw it that far. But if I pop over here to Safari, I am able to throw across multiples. See how I could skip some there. Yeah. All right. So scroll snap always is trying to say, ah, we shouldn't allow that. Let's make sure that that stops. And I think that's a good feature, especially if you're wanting users to go through their users one by one. Anyway, turn that off if you want users to be able to flick and skip their friends. Anyway, kind of a cool feature of scroll snap point. Okay. So that's our stories and our users. We made the columns, each user snapping to the column and space that the story parent like container created. Now let's talk about users and why are there, you know, why are there stories stacking on top of each other? I don't see a position absolute anywhere. Let's check out how I did that. So first it starts with display grid. Again, my favorite, you're going to see me do that a lot. And it's more shorthand because I'm a shorthand junkie. And I think this is really cool. What this is saying is I'm a grid of one cell or one row and one column. Each of them are one FR. So they're taking up basically 100% of the space that they're getting articulated here by their parent and name each of these. We want to name the row story and the column story. And that seems kind of weird at first until we see how the story works. I'm just going to skip ahead right here into a grid area story. Now this means that each of the stories is going to try to fill that one by one space by claiming the story cell. And here, if I show that in here, pop down into this user section here. Look, I have three different stories here stacked on top of each other because they're all claiming this grid area story. If I uncheck it, we'll see that. Yeah, see, here's one. I don't know. The other one is down here. They're all crunched. They try to get auto placed, but once they're all claiming the same space, we now see them there. And we're also letting grid do the zindex stacking. So we're not doing that. That's all coming from the DOM. There's no zindex tricks in here, which is I thought was really cool. All right, so that's how that's working is our user class here is saying, I'm a grid, but I only have a child of one cell. And all of the children inside of there, so there's multiple children all claiming that same cell. And that's why it's making them stack. And I called it grid area story. You could have called it grid area stack or something like that. But it's just kind of fun, right? You get to custom name it. And after we do the grid area story, we're pretty much done with the majority of the scroll snap points and grid layout here. At that point, you can pan through your users. Anyway, it's just really nice. You have the majority of the work here, and then we can kind of add some flair. So I want to talk about some of the flair I put in there and a little bit about the JavaScript that I wrote. But this flair here, background size covers, that's making sure that all of our story images are filling that space that is of the story here. Our background image is a cool trick. We have a multiple background layered image where the furthest layer back is this linear gradient, which is like a soft gray. I'm using LCH. You can go study a CSS podcast episode I have about that with Yuna. She's awesome. We talk about perceptual colors and why LCH is cool. And what's interesting, so this is layered. This is the background one, right? That means if we have a BG, which we specified on our HTML here, right? Here's our BG. If we take that custom property, which is a URL, and we pass it right here, we're basically just saying our topmost image is a URL, and our backmost image is this gradient. And so we kind of get placeholders for free. We get this nice looking tombstone or skeleton gradient while the image is loading. And we don't have to write any JavaScript for that. CSS will automatically notice that this URL is done loading and finish its paint and complete the drawing, which is super cool. And I love that effect. I think it makes for a really quick and handy type of placeholder for loading. Then I have user select none, and I have touch action manipulation. And what those are, so user select none makes it so no one can select the text of a story. So if someone put text on there or whatever, and it also sort of makes it so that if they tap and hold and other sort of gestures that they won't get any native sort of document type behavior, we're disabling that we're saying no this typical document behavior we're going to set to none. We are going to say the touch action those manipulation. And by doing that, we remove the click delay because we're telling the browser we're not going to be competing with any links here. We're taking over and we want this to be a very tangible touch interactive space. So by setting touch action to manipulation, we're saying, give me some instant tap love as I go through these stories, right? Who wouldn't want that? And we are transitioning here. So this was just some extra flair. We didn't have to do this because most stories don't transition as you go through them. But you can see that the effect was kind of nice, I thought. So if I go back through here, see that little fade between stories? I thought that was cool. So anyway, the way that I did that is I just said transition on the opacity over about 0.3 seconds. And here's a nice little easing function that came from material. And down here when the item has been seen. So if it's on its way out, we're going to set its opacity to zero and pointer events to none. And that essentially lets the next story be clickable. And it disappears and reveals the story underneath it until we hit the end at which point my JavaScript code takes us to the next user. So that essentially handles the HTML and the CSS for this. And I'm just going to breeze really quickly over some of the JavaScript. We've got a selector here for the stories component in the DOM. We've got a median here. So this looks at the stories element, looks at where it is on the screen and tries to decide where the middle of this element is. And that's going to come in handy when we're looking at our clicks for right and left. And we're going to try to determine if we need to navigate forward or backward. Then we have some state stored right here, which is the current story. The current story, we discover it this way. We grab the first element child and the last element child. So this would be like stories, your first user. And then the most recent story that they put on there, which will be on top is the last element child. So that's the way the stacking is working. We have a function called navigate direction. And that just takes left or right. And by passing that you'll navigate through the stories element. It does all the logic in here. You can pop that open in the open source code if you want to go see it. We also have two event listeners. So these are our things that are having our users navigate through the story. It's the first one here. Stories.out event listener click. This one is looking for here. We'll just expand this out. We're just looking to see if it's greater than the median. If it is, we want to go next and if it's not, we're going to go previous. Nothing too complex there. And the keyboard event was the same. We get left and right for free is mentioned here in the comments, but we don't get up and down for free. So up and down would be the one that changes the opacity and essentially is determining next or previous. So we look at the keys getting pressed and if it's down and if it's up, we'll go and pass that on to the applicable function here. And we'll look if it's arrow down, we're going to go next and if it's arrow up, we're going to go previous. And that concludes the whole workflow here. I'll just do a refresh as we breeze through the Envid here and it gives like a quick little breakdown. We had stories that created columns. We had each section created a single cell for its children and all of those children tried to claim that single cell. So we had a grid inside of a grid inside of a grid for this multi-dimensional array of content and all of that just so that we could oh look at that just so easily pan through our content here as we swipe through or we can click to go through just as we would inside of a normal application. So I thought that turned out really sweet. I want to see how you build it. I want to see maybe you build on top of this because look, I don't have user avatars. I don't have any other metadata here. You could totally put content inside of that story and it would render. So you're ready to add padding and really like work inside of this space as if it was in flow, which is I think really nice. So thanks for watching this video. I hope you learned something new and I want to see your challenge. Show me what you got. Let's show me how you want to do this. I'm really interested. Let's grow together. Cool. Thanks for watching this video. I'll see you all later. Bye.