 So far, we've had Serma and we've had Jake talk about performance and streaming and stuff. And I wanted to talk about the bit that happens after that, the bit where you have your UI elements. And before I get into it, I kind of wanted to put that into a little bit of context. When we talked about performance, historically, on our team last year, year and a half, we've talked about a thing called RAIL. And RAIL stands for response, animation, idle, and load, and it's a way of thinking about performance that puts the user in the middle and lets us figure out what they expect from things. So, for example, if you tap on a screen, you expect something to be coming back on screen in under a tenth of a second. If you scroll or there's a transition, that's an animation, and we want that at 60 frames a second. Idle is a bit of a funny one, but the general idea is that you should do work when the user isn't interacting and when there's opportunity to do so when the main thread has some time. And we do those in 50 millisecond chunks so we can keep responding to users and loading. You want to keep the user on their train of thought, and so you've got about a second to get something up on screen, whether that's from a cache or the network. Now when we talk about RAIL, this is the kind of world view that I think many of us have today. We kind of go, yeah, response is okay, yeah, we don't want it to be laggy. That's fair enough. It's kind of important. I've heard that's a thing and that's something I want to make sure is good. Idle. Yeah? Sure? Seems good. Load. Load. Yes. Back in the safe. Okay. Load, yeah, minify, concatenate, all that good stuff. But here's the thing. This is kind of where we're thinking of being on people's home screens. And a closer look at that home screen raises an interesting question. Which of these is the progressive web app? Which of these is the native web app? The truth is, users don't care. What they care about is that the thing that they tap on works really well. And they would never say something, oh, wow, it skipped things. There you go. Back would go re-wide. Okay. Nobody's ever going to say this. And if they did, it would sound like this, that up looks like native, I hope it behaves like it. There you go. Behaves like a native app. They expect your stuff to run well. I want to say behaves. There we go. Hello. Behaves for me is like performance. And it's about does it behave as we expect? Does it do the things that, you know, when I interact with it, does it do the right things? Right. Since that's the case, I think we can re-evaluate RAIL to look more like, back, bad clicker. More like this. The response part of RAIL. While we expect our responses to be instant, like every time instant. So that's just now more important. Animations. Yep. They're down there. And there we go. They're going up there. Because we expect, again, we expect instant transitions. We expect, you know, scrolling to be super smooth. IDLE. Well, now, if we're doing more work to make responses and animations good, IDLE is something we're going to have to be more tactical about. We're going to have to do some work when the user isn't. And we have things like request IDLE callback for that. So IDLE just went up. They're all going up. Good news, though. I think if you're expecting somebody to add you to home screen and run your stuff lots of times, hopefully you're going to have a service worker. Hopefully you're going to be running from a cache. And therefore, I think load drops down to here. I don't think it's unimportant. Don't get me wrong. You've still got to do a good job to get that first load there. And I think over time, I think that's what we're talking about. So if you're being home screen-minded, I think it looks more like this. And I think if you think about the native apps that you run, you have a similar expectation. You don't ever sit there thinking about, oh, this APK took a long time when you're using it for the 50th time. You're thinking more about how this actually behaves, whether it's got the features you want and so on. So with that in mind, I wanted to talk about three individual components. But they give me an excuse to talk about a bunch of other things. And I can explain, I suppose, the way I categorize my components from deterministic. We know upfront. We can hard code the values for those animations and interactions through something that's a little bit less known upfront, all the way through to something where we've got no idea about how it's going to behave until somebody actually clicks on the thing. We have a little bit of an idea, but not much. So those three components are a side nav, some swipeable cards, and an expand and collapse view. Let's jump straight into the side navigation, which I think most of us have seen before, seen these. Yeah, we've seen these quite a lot. So what I'll do for each one of these is I'll explain the theory of how I would approach them to maintain performance and to kind of be performance minded. I will be leaving probably some glaring omissions in the area of accessibility, but just as well, Bob Dodd's coming on next, and he's going to talk about accessibility. Oh, it all fits together wonderfully. Anyway, the theory for the side nav, what we're going to do is we're going to pop on a containing element over the top of all our content, into which we can place a semi-transparent black background to obscure our content. And then we're going to have this content bit with our actual side nav in it, which will slide in from the side, like so. So the CSS for something like that, for that containing element, it's going to be position fixed, left zero, top zero, width and height of 100%, some people like to do right zero, bottom zero. That's fine. Works just as well. Overflow hidden because we don't want any scroll bars, but the pointer events one is a bit of an interesting side step that I want to take. It lets me talk about something that's kind of like a primed element. So these are elements where you want that thing to be ready to go. And a side nav is one of those because when a user taps on the button, they expect the side nav to just come through out. So the general idea behind a primed element for me is that it's something that could be activated at any time. Like a side nav, yeah, it probably fits that bill. And if you were to toggle its visibility, it would take more than 100 milliseconds, because they're probably going to tap a button and the rail tells you you've got 100 milliseconds to respond. If you take longer than that, it's going to feel laggy. So these are my sort of two criteria. As I say, I'm leaving a glaring omission as regards accessibility. So hold onto your hats for that bit coming next. But all the same, we have a primed element. I think this is a primed element. I think it fits. And as such, we can take a couple of shortcuts. One of which is we're going to promote the content bit to its own layer. And if you're not familiar with layer promotion, the idea is you want to separate out an element from the rest of the page so that when you paint it or move it around, you don't affect any other element on the page. It's the same kind of deal as, you know, if you add an art package or whatever and you create a layer and you can sort of mess with the pixels in it and you don't mess with anything else. Now, the easiest way to do that today to create one of these layers called a compositor layer is to use will change transform. So if you imagine the simplest possible page with a photo of a bald idiot and an ice guy, and you put will change onto said image, now you can move it around with a transform. And you can see that it's kind of separate from the page. Obviously, this isn't happening in real time. But it's the idea of separating these things out. Now, you may be sitting there thinking, goodness me, that seems like a great idea. A star will change transform. Burn that from your mind. OK, if I'm not clear, don't do it. The reason you don't want to do it is twofold. Firstly, you'll want to keep your memory usage down, especially on a mobile device. If you create layers, you're going to use memory. You're going to have management, you're going to have textures on the GPU, all that kind of stuff. So you want to do this as needed. Now, with the prime element, I'm going to make the argument that you probably want to do something like the will change in your CSS. But in other cases, where it's not known, until you start interacting, you probably do the will change in your JavaScript. The other reason is you want to keep your time in compositing to a minimum. Compositing is where we take all those layers and we squish it back together and put the pixels up on screen. Now, of course, if you made lots of layers, that's a lot of depth sorting. It's a lot of management. And it's a lot of putting back together. And that takes time. So you want to be tactical about this. So we've got our promoted layer. And as you can see in the CSS, I'm going to put will change transform on it like so. And then come back. Then I'm going to transform the contents off to the left by a oddly specific 102%. And if you're curious about that, it's because I've got a shadow. And I just do an extra couple of percent to hide it. Cheating. But that's programming. It's just cheating sometimes. And I'm a cheat when it comes to programming. Very not otherwise. Especially not when playing little games with my kids. I'm very fair. Never cheat. Anyway, eventually, the user is going to tap on a button and that's going to show the side nav, which in this case is just going to add a class. And that class is going to remove that minus 102%. Fairly straightforward. And we get something like this, where it slides in from the side. That semi-transparent black background is the same kind of deal. Here, we're going to do a will change of opacity from an opacity value of 0 to an opacity value of 1. And getting rid of it like so is just going to be the same in reverse. We're just going to remove that class. Everything goes back. It's great. And we can just do that with this hide side nav, which I put on the containing element. So if you click anywhere, I'm going to hide the side nav, which is a bit bad if you actually click on something in the side nav. So the way to get around that is to just add an extra handler for that particular situation, which cancels the click, which is just a stop propagation. So I'm kind of cancelling the click. And it works out really well. In fact, so well, this is what it looks like in reality. This is one that Serma and I built. This is actually running on a Nexus 5X. And you can see, slide out, slide in. I actually added a bit of drag thing to it, which you can see if you want to afterwards. When you take a profile of this in the DevTools timeline, which is kind of what you want to be doing with all your UI elements, I have the side nav sliding in and sliding out, which doesn't look very big, so let's zoom in. You see that green chunk is the frames per second, and we're hitting a nice comfy 60 frames a second on a Nexus 5X. And below it is the amount of work per frame, which is pretty low, because we're not doing much. We're just using transform changes. If you're interested in seeing that actually being built, for real, there's the TLDW, and there's also a livestream, which was about an hour long with me and Serma where we built the side nav, bugs and everything. It was great, but you can catch that if you haven't seen it. So let's move on. Since we've done the fully deterministic minus 102% to nothing, we can move on to the swipable cars, which is a little more interactive, a little more dynamic. So that's this one. You've seen it probably from Google Now, you know, cars, and it slides up to take its place. The theory here is, again, we want to promote to a layer for the thing that's being interacted with. But we want to do that on demand. We don't want to do this one ahead of time, because if you had a lot of cards, that's a lot of layers and a lot of memory usage, not a good idea. We also want to use transform and opacity as well, because we're going to transform this thing off the side. And from a behavioral point of view, we want it to fade out, because that gives the idea of being dismissed. It's kind of something the user would expect. Now, at this point, I want to take a little bit of a detour and talk about kind of game loopy stuff, which is something that any game developer would be like, yeah. And it's extremely useful in this situation. What we want to do in these kind of cases is decouple our input, which can happen fairly sporadically and whatever from the actual rendering and drawing a bit, which you'd expect from a game, because if your character stood there, you don't want to, you know, you want the kind of game to keep moving, even when you're not moving the character. Right? Same kind of thing. We want this animation to keep going, even when we're not actually doing touch events, for example. And the way we do that is we call requestAnimationFrame for every frame of the interaction. And we'll give it our update function, which as a nice side effect, gives us a function that we can just call to be like, just draw it, even when there's nothing else going on. Now we've got a touch move at the start of the frame. Well, that's OK. We'll just use it. We'll just store its value, and we'll pick it up in the requestAnimationFrame. If it comes in a little bit late, or it doesn't come in at all, how big deal? We'll just use the last known good value. If by some weird, weird chance, we actually got two or more, well, again, we're not doing work per input event. We're only going to do it once per frame, and we're just going to use the last known good value. That's good. So this is a model that you probably want to adopt if you haven't already for this kind of work. Now we get to actually adding the event listeners, and fair enough, I'm going to share them between mouse and touch. I think point events might help a little bit here, but I've got this. So touch start, touch move, touch end, and so on. And as a side note, the adding of these touch handlers to the document is bad, generally speaking. And the reason it's bad is this, really. You have the compositor thread, which gets the user touch. It's the one that actually is told about the interaction first. And by default, it would do something like scrolling. It would just move the page up and down. But we registered a touch move, and that involves the main thread. And if the main thread was busy or our touch move just ran for a long time for some reason, then eventually it'll come back and the frame will be shipped. But in between that, we've blocked the user. Oops. So that's not as good. And there is a way around this. It's new as of, I believe, Chrome 51. And it's in Opera and Firefox. It's in development according to Chrome status. Same with WebKit, but this is encyclopedia large, obviously. And I'm not sure about Edge. I can check with the folks on that team. However, what we do is we add this passive true to the event list. Now, one that says is, I won't call prevent default. I'm not going to do it, so don't worry about waiting on me in order to do the thing you were going to do. Still give me the events and give me the information, but I'm not going to prevent default. And the browser goes, cool. I won't block, then. I'll just get on with it. That's great. So anyway, back to where we were. We have our event listeners, and we talk about what they are going to do. For example, we have this card. It's in the startX position, so what we'll do is you've tapped on it. We'll go to our onStart, which is going to basically ask for the position, which will either be pageX or the firstTouchEventsPageX. And then we'll add willChange on it dynamically, which will give us a bit of a hit because we have to create the layer and everything, but it's probably going to be OK. Probably. Now, you actually move your finger across, and we have to kind of go, well, what's the new position? Which is fairly straightforward. It's the same kind of deal. We just track the pageX or the firstTouchEventsPageX. And since we know where we were and where we are, we can figure out what the translation should be, and we can use that in our update function. We can say, if you're dragging the card, your translation is the current minus the start, and what we will do is we will apply a transform with that value. Great stuff. That's going to work. We're going to be able to slide across. We can change the opacity in the same kind of way. But we now need to think about the next part, which is I'm kind of dismissing the card, right? Or I don't go far enough, and it comes back to the middle. This is the behavioral bit. So if we consider a normalized distance, so it's at zero, if it was out to the side, we could say that was position one. Or back in the other way, that's also position one. What we can do is we can kind of throw up these thresholds. I put them at 0.35 when I was doing this. You could pick 0.36 or something else entirely. It's very exciting. What will I choose today? Who knows? That's Web Dev. So I put 0.35. And if you don't go past 0.35, I'm going to slide back to the middle. If you do go past 0.35, I'm going to dismiss the card. Fair enough? OK. So that's in the on end, like so. There we are. Threshold. Card width times by 0.35. And this is this target X value, which we're going to come on to in a moment. It's defaulting to zero. If you go past the threshold, we're going to choose either the card width or minus card width, depending on which direction you are going. And we can pick that up in the else here, where we say translate X plus equals target X minus translate X all over four. And if you've been around for a while and done this kind of work, you'll probably recognize this, if you haven't, very exciting little one liner that is incredibly helpful when you want to do what I call the easiest easing in the world, or easing, easing, easing. It always takes this form. It's value plus equals target minus value all over strength. And I genuinely have made other developers memorize that, because it's incredibly helpful. Let me show you what I mean. It kind of worked example. Let's say you want to get this box from zero to 100. And you basically target minus value, so that's 100 minus zero, all over strength, which is four. We'll move it 25 pixels. Cool? On the next iteration, it's 100 minus 25, which is 75, all over the strength, which is four. That's 18.75. And as you build it up, you're going to see that it's going to slow down. It gives us a nice kind of slow down easing feel to this, so that your box would go. Sound effect's not included. OK, so that's this one line. But it's an incredibly helpful line. It's either going to ease us back to zero, or it's going to ease us to the card dismissal point. Now we need to detect doneness. No better word for what we're doing here. Is this animation done? Can I say that we're finished with this? Well, OK. The way we do that is, well, it's either you go there and back to the middle, fair enough. In which case, we can just say, are you nearly at the start? If so, yeah, you're probably done. That'll work. That'll get us most of the way there. If you are, just reset the target, allow the user to interact again. Other one is, basically, you've done the slide out to the side like so. And we also know that we're going to fade out the card. So I mean, this is pretty cheeky. But we can just ask, is the opacity really, really low? If it is, it's nearly invisible. If it's nearly invisible, it's going. It's going, going gone. And in fact, gone so much that we're going to remove it from the DOM with remove child, like so. Now that will cause all the other cards to jump up immediately. Because the DOM, we took out an element and saw the other ones went, cool, there's some space. Which we don't want, we want them to animate. We have this function that says as much. And it looks like this. You know, that kind of slide up. There you go. That's the thing you want. So what we do is, in our animate of the cards into position, what we're going to do is we're just going to ask for the current card. And we're going to step through all the remaining ones, like so, and we're just going to basically push them down, straight down, by a card's height. So you go back down to where you were, please. Just ignore the fact that we just removed a card. Do you stay where you are? And then what we're going to do is we're going to wait a frame for that to take hold, because styles will run after the end of our JavaScript. Cool. And then what we'll do is we'll switch on a transition, on transforms, and we'll get rid of it. And that will cause all the cards to go zoop. And that will look great. Cool. And then when we're done, we can reset the target. And in reality, this is what it looks like. This is another one that Server and I built. Again, it took an hour. There were bugs. Weird. So you go slide, swipe, it's great. If you go to Timeline and you were to take a recording of that, again, zoom in. And it's actually in two parts, this one. The left-hand bit is the card dismiss. What are you doing? Slides. Don't do that to me. Maybe it's me and Jake. Maybe. Maybe we just, there's something bad going on. I'm blaming Jake. It was all fine until Jake came on stage and everything broke for him. Interesting. Coincidence? Yes. So the card dismissal, that's right there. And then there's the other bit here, which is sliding the cards. And you see there's a little dip at the start. And that's because that layer promotion of, or there's a bit of layer promotion, but the kind of setting up of those card animations did cost us a little bit. So if you're interested in that and you want to see that one built, what are you doing? Seriously. OK. If you want to see that one built, there's a TLDW. There's an hour-long live stream that you can catch of that one as well. Swipeable cards. Right. We can move on seriously. I'm just going to stand around here. OK. The Expand and Collapse is the final one. And it's the one that is the kind of fully dynamic one. Because you could pick any of those cards. They could be anywhere on the screen. And OK, in this case, in the mobile case, they might do a full-screen takeover. But they might not. They might just expand a little bit or something like that. And we don't really know ahead of time. We can't hard-code those values. And if we try, it's going to be pretty horrible. So how do we handle this situation? Well, the theory for this one is going to be a little more involved. But that's fun. When I get something like this from a designer or I do it myself, I kind of watch it over and over and over again. It's a thrilling few minutes. But it's a useful few minutes. Because as you kind of watch something over and over again, your brain starts to notice the patterns. And the patterns here are, in this case, I think that thing is getting bigger and it's moving. Yes, there's the pink head a bit that's fading in. But predominantly, this is about a movement. It's getting wider and taller and it's moving. And therefore, I would normally go, wow, that feels like a width, height, left and top moment. Great. That's probably what I would animate, except that that would be bad. And the reason it would be bad is that in every browser, you would trigger layout with the purple chunk at the top there, paint and composite layout is basically where the browser says, where is every element? It's basically a geometric process. Where is every element? What is its size, and so on. Paint is where we fill in pixels and compositing is where we put the page back together, all those layers. Now, if you've got to do that for every single frame and you might have a reasonable size to DOM, you're in trouble. You don't want to have to do this work on every single frame. Chances of getting 60 frames a second, slim to none. You may have noticed that I use transforms an awful lot. And the reason is their profile is different for an element that's got its own compositor layer. Changing a transform is not going to trigger layout and it's not going to trigger paint. It should only trigger compositing, which is something that we can probably get done comfortably at 60 frames a second. So this then changes the question to look like this. Can we do that effect with transforms? Can we remap this? Slow mo. Can we do that with a transform? Well, to me, that looks like a scale. And it looks like a translation. My approach is called flip. That's the first, last, invert, and play because there aren't enough acronyms. OK? So I'm adding another one. Cool. But it's an extremely useful way to think about the animation. What we want to do is we want to essentially ask the question, at runtime, where is the element that I'm interacting with? So we record its first position on screen. And we do that with something like get bounding client rect, which is fun to say and has been around since IE4. Brilliant. And it will tell you, in relation to the viewport, where this element is. It's left. It's height. It's top. It's right. It's bottom. All the stuff that we need to know. Now what we can do is we can actually snap the elementing question out to its final position. Now I'm doing this with a class, but you could manipulate the styles. You could do whatever you need to do. So now our card is going to be in its last position, like so. And we can call get bounding client rect a second time. So now we know where you were. And now we know where you're going to be. That's cool. That means we can start to kind of figure out our transforms that we might need dynamically. Now there is a word of warning here. Going from first to last is going to trigger styles and layout. And the reason it's going to trigger styles and layout is because the second get bounding client rect came after some style mutation. We said, here's a new class for you. Or here's some style changes. And then we asked for how wide and how high and where are you on screen. And the browser goes, I don't know. You just moved everything. Hang on. Let me go and figure it out. And I'll come back with an answer. And that's exactly what happens. So you've got to bear in mind that from this first to last. And you might be sitting there going, hang on a minute. I'm sure you said triggering layout was bad. And I did. But the key is here, we're not going to do it on every single frame. There are two things we need to bear in mind. One, we're going to do it once at the start as a setup cost. Secondly, we have rail, which is going to be our friend here. Bear in mind, the user tapped on a card to get the animation. Therefore, in rail terms, they're here. We have a 10th of a second in which to respond. We have a 10th of a second in which we can do some work. And believe me, a 10th of a second is actually quite a long time. Especially when it comes to this kind of work, it's great. We should use it, and we do. So when it comes to rail and flip, you can typically afford to do a single styles and layout pass. Seriously, one. But that's cool. That's often enough. And in terms of flip, that's a good setup time. You'll still need it to complete in less than 100 milliseconds. So you kind of have to be aware of how big the DOM is. And if you're able to use something like CSS containment to limit the scope of layout and paint, you should definitely do that. And that's really useful. However, we knew where we were. That was first. We know where we are. That's last. And now we can transform. What we'll do is we'll just basically apply an inverse transform to take us back to here. We can do that. So first minus left, blah, blah, blah. Do that with a scale. Apply a transform that uses those values. So at this point, we've done first, last, and invert. And it's like this. So if I was to tap on the card, ready, steady, go. There you go. From a user's point of view, nothing happened. What's really happening is this. First, last, and then we're inverting. And it feels like a lot of setup cost, and it kind of is. But it gives us a huge advantage, because what we can do now is switch on transition on transforms and remove that transform. And our card will just go, pfft. We didn't know where it was, but at the start, we didn't hard code it. We just said, where are you going? Where are you now? I'll figure out the transform, and I'll apply it for you. We've just managed to remap something that was width, height, left, and top, which have run at 60 frames a second, just something that definitely will, hopefully, all being well. Caveats, though, because there's always those. If you've got some scale changes that are being applied, something like text. Let's say you're doing something flip-like, and you've got something with text inside, that might get squashed or stretched. So you might need to move the content to a sibling element so that it's not affected, and then just faded in or something like that, but a sleight of hand. You might need to do that. Bit of gymnastics, but it's well worth it. Like I said, the first to last does involve forcing styles and layouts, so you have to be careful with that. But this is what it looks like in reality. This is a little kind of expandy, collapsey card thing, which that's running on a Nexus 5x. And this screen doesn't make it look like it's 60 frames a second, but it is. And I can prove it because I've got timelines that show it. And you can see it. I'll show you. It's great. It's also responsive, design-friendly. Like I said, we're going to ask at runtime what the first position is and what the last position is. We don't hard-code those values, which means the same animation on desktop looks like this. Different position, different sizes, but still the same stuff. And this is what it looks like in timeline. We'll zoom in again to the top bit. And you see the dip at the start, and you see those little red markers. That's DevTools telling you your frames per second dipped below the comfortable point of 60, but we know that that was the setup of Flip. That was the first lasting invert, which on a Nexus 5x on this case was about 40 milliseconds. After that, that is a steady 60 frames a second afterwards. Tremendous. Cool. I get to call this one a day in a moment. Some closing thoughts. You've noticed probably that I use WillChange, and I would suggest that you start doing that if you haven't already for elements that you intend to animate. You need to decide, is this thing primed? If so, I'm going to probably put the WillChange into my CSS. If not, I need to do it on demand, probably via JavaScript. But you want to use it sparingly. Don't go overboard. Transform and opacity are your best friends when it comes to UI elements running performantly. I hope in the future to be able to say that you can get away with a lot more. And actually, we are heading to that world with things like GPU rasterization, CSS containment, lots of really good things that might help us limit the work and mean that we can do more. But for today, for the cross-browser story, I would suggest that you stick here. If you find yourself in the dynamic end, then something like Flip, where you can remap expensive properties, calculate your transforms at runtime, is very useful. Now, follow that's news to you. And you've never come across layout, recalc styles, or anything. Here are some links inbound. Phones out. Don't worry, you can get the slides afterwards as well. All the phones went down. Don't care anymore. There's the Google Web Fundamentals render performance section complete with Udacity course, which will take you through the same kind of content. Very useful to get up to speed. Secondarily, if you want the source code for those elements I showed, you can get that at the Supercharged UI, which will take you to the GitHub repo. If you want to see me and Serma, where Serma basically spends an hour interrupting me, and I try and code, it's like real life. There are bugs as well. Every time I do one, it's scary but brilliant. There's a TLDW, which is like five minutes, if you haven't got that amount of time. Right, I need to shut up and move on. But before I do, I just want to say this again. We are hopefully going to be here. This is the first time we've been invited to people's home screens. That is incredibly exciting. But it's also a huge responsibility. We need to act like we deserve to be there. And that means taking user interface work super seriously. The web is ready for us to do that. We can do that today with the tech. I've shown that you can. I hope that you go and give it a try. And with that, I'll say thank you very much.