 So, usual rules apply. I want to be super clear on this bit as well. These are prototypes. We build prototypes in this show. We're not trying to build production code. It's a very important distinction because what we're not doing is dealing with every aspect of building these components, like accessibility. We're gonna try and do a little bit of that today. Make sure that's at least decent and in a really good solid foundation. We're primarily focusing on the performance aspects and just trying to get a solid foundation and think about these problems in the right way. But if you're gonna take this code onto production, you do have more work to do. You do need to think about some of the things. And you will be judged by us. No, you won't. By him, maybe, but not by us. I, no. Anyway, so. I actually wrote one of these components on one of my first home pages that I use in production. And my thought would be a great idea to animate min-height or max-height, I think. So that means on every frame, the whole text inside the box would be relay-outed. And I just thought that's the best I can do on that. So I just shipped it. And we're gonna do better today, right? I don't know. We'll find out. Right, we're gonna find out. Okay, let's dive into some code. You've heard me waffling on long and long enough. Of course, as usual, if you want to involve yourself, you can get on the chat with Sirma. He'll be around, making sure that there are links posted and everything else. Right, to the code. I have an HTML page sat here. I've made myself a couple of notes on things that I wanted to remember on my way through. And one of them being Lorem Ipsum. Lorem Ipsum. Because what I want to make sure is that one of these panels has got a lot of content. Oh, yeah. Because we're gonna have to account for the fact. It's very easy, isn't it, to ignore long content. And just be like, yeah, so all my content will be no longer than 30 characters or something. Right, we'll do a custom element, I guess. So an SCE accordion, why not? Supercharged accordion, woo. There we go, we'll close that off. And I guess we need a bunch of... So we are doing the whole custom components, custom elements approach again. Yeah, why not? We're making UI elements, making our own components. Feels like custom elements is a good fit for that. But if it's done... Eventually they should be usable again. What we're producing is not really supposed to be reused. Yeah, right. Yeah, exactly. But eventually you want to have elements. It's hopefully a solid foundation. So you could take that on if you wanted to. So an accordion has a bunch of panes or tabs with pain, but I'll call them an SCE pain, I suppose. Let's say I want a bunch of those. And each one, let's give that Well, so he's got to have the header. So you can click on like one, two, three, four, whatever. So I'll tell you what, we'll make it a button. And, yeah, I'll make it a button. Give it some hammer one. We'll just do this for now. So we're gonna have like, let's say three of those to begin with. We can add more later on if we want. So, now I made myself some notes. Each of these from a... If you've never seen it, by the way folks, there is this on the W3C, there is this Aria authoring practices doc 1.1. And on the left hand side, I mean, if you've not seen this before, Rob Dodson talks about this in his accessibility talks. From IO. Yeah, from IO. And also from the Progressive Web App Summit in Amsterdam. And also it comes up in the Udacity course that Alice Boxall and Rob Dodson did. So if you're not caught either of those, or all three of those, I guess, two talks and a course, those are there, they're available for you. You should definitely go and check them out. I'm working my way through the... And they put a lot of effort into the Udacity course, so it's really high quality course that you should take the time to watch because accessibility is important. Yes, and one of the things that came up from Rob was that there's this document anyway, and in here is this thing about an accordion, and it'll tell you what's expected, like all the keyboard interactions and stuff. And they have this huge list of all the common components that you usually encounter when doing a website, and it tells you what you have to pay attention to when you wanna make these individual components accessible. Cool, so alongside the other stuff that we're gonna try and do, I'm just gonna try and make sure that we do at least cover off some of the basics of what one might expect, at least like keyboard controls and tapping and stuff on my way through. But I think Rob's gonna try and pick up where we leave off and make sure that it's all just... Having made Udacity course, he has much more knowledge about the intricacies. Yes, he does. Right, so one of the things is we need a role, and the role that we want apparently, I think, for this is a tab list. Each one of these is gonna have a role of tab panel. And then inside each one of these buttons, we're gonna say that's a role of tab, I think. I think, I think. Yeah, okay. So I guess each one of those needs the same deal. And also the tab panel as well. Why not? Let's do that. And we're gonna have to give them a tab index as well. So apparently if you just want it to show up in the tab order, let's start tab index is zero. Not like zero, one, two. But it means that something that's not focusable will now be focusable. Correct. Because by itself an SC pane is just nothing. I'll put it on, I'm gonna put it onto the button because that would actually be tabbable, wouldn't it? That should be tabbable. Let's find out what happens. Let's leave it out for now and just see what happens. Right, so let's get into some styling. So just because I found it very confusing, these roles, tab list, tab panel and tab, have three different meanings attached to them. So the tab list is the list of all the tabs. Just think of the tab bar in your editor. The tabs are these tabs and the tab panel is the content that is associated with one tab. And that's why we have three roles. At first I thought they would be really redundant, but it kind of makes sense to have these distinctions. Yeah, I mean, again Rob would be Rob or Alice would be the right people to talk about this. I assume it's because of the screen readers make of these roles basically when they're looking through this. Anyway, I'm going to wrap this. In fact, what I want to do is I'll move that to a different part of the document. But basically, you can see I'm setting up myself some content inside, but I'm going to move it this way. Good old Laura Mipsum. Hey, it works in this context, so I'm sticking to it. So, Div, this is again, is it Emmet that people said I should learn? Yeah. Every time. Every time. Still not doing it. No, it turns out I actually have other things on my plate that prevent me from doing that right now. All right, this is some content. That'll do it. That's a different amount of content. Yeah. OK, well, this is some more content then. Yes. Vastly different from panel one there. Got to cover all the possibilities. OK, so by default, let's do, are we going to need some styles as well? So let's drop that into the top. No, don't do styles. Rare equals style sheet. OK, href equals styles.css. I mean, I shouldn't, I mean, I mean, I mean, come on. I could have come up with something better than styles.css, but I'm sticking to it now. All right, fine. So styles.css. CSS. OK, let's do body. Let's say display flex direction column. Why not? And then we'll do what we're going to do. Let's do, I'll be a line items center justify. I mean, if this works, I'm going to be so happy. I mean, I'm always guessing with this stuff. Yeah, flex box is not something that you can usually just remember very well. I offend myself. But it looks like it's actually kind of done the right thing. OK, so let's now, let's see, sc accordion. We're going to do that with say, I'm going to tell you, we'll do with 100% max width. Oh, look at you being responsive. I know, right? Trying to get it, like I said, trying to give a solid foundation. So we're already starting to lock some of this in. You see what I'm trying to do is I'm trying to style this so that if we didn't have JavaScript, this content would stack up one on top of the other. And I think that's. I think they call it progressive enhancement. You'd assume nothing and then you'd progress. No, I know, I'm sorry. Dude, that's fine. Good, right. So sc pain, sc pain. We will say, la, la, la. Oh, I'll tell you what, let's add this. Box shadow. Box shadow, box shadow, box shadow. OK, naught, naught, no, naught, three pixels, four pixels, rgba, should give us a nice little, yeah, that'll do it for now. Justify content center. Oh, do you know what it'll probably be if we do like height, 100% width, 100%? I usually use vh and vw there. 100 vh? I'm going to use the new tools. It shouldn't change visually, but it's just like semantic. Yeah, you're good. Yes, I am. OK, great. Thank you. OK, don't worry. It's really warm in here, so I'm going to upgrade to just the t-shirt, folks. All right, feel better already. Do the crazy knuckle cracking thing, which I'm sure everybody just loved. So we just got a question. What is up with the SC elements? Oh, cool. So you have to, with the custom element, you have to have a dash in there. So you need like x-something, x-accordion. SC stands for supercharged, because it's custom. We're like, why not go full custom and drop SC elements? We put our branding everywhere. The reason I think originally it was behind the dash is that you can avoid conflicting with the native elements. So names without a dash are reserved for native elements for the browser, and everything else is user land. Cool. So there you go. That's why we do it. FC, FC, FC. That's not even a pound. Hey, hey. There you go. FC, there we go. Isn't that even a thing? I don't know. Shouldn't it be back on color? Oh, yeah, man. No, you can do either. Yeah, you can do either, because it's the shorthand. So it'll expand that. Anyway. People want a padding on the accordion. Yeah, I know they do, but they all have to wait. Wait, I want it too. I mean, I'm looking at it going, that's awful. I'll tell you what we'll also do while we're here. We'll do a border radius of three pixels. OK, three pixels. That's what happened when we typed it. And we'll do overflow hidden. And then we'll do max height, 600 pixels. And you might be wondering what I'm doing. I don't know. I'm kidding. I'm kidding. I'm kidding. Right, here's the thing. The default styling for the accordion. I think we only want to do the max height thing and the overflow hidden, these two, when we're sure we're going to be able to build it with JavaScript. So let's do SCAccordion Enhanced or something like that. And then we'll set that like that. So without it, it should just kind of flow whatever height is of the content, which is fine if we can't build this thing. What we can do is we can just chuck in a bit of inline JavaScript here. Script. And again, this will be something you'd probably do a little more neat and tidy. But I'm going to do it inline because I don't want to wait for a network request for this to complete. So I'm going to say document.querySelector. That.setAttribute, EtudeEnhanced, and then an empty string. And all being well, there's no real visual change by the look of it. But we do have an enhanced attribute there. OK, so that's a good start. OK, so back onto the actual styling of these things. So the SCPane, we've got anything for the header. We have a button which has a roll. So we can say button.tab, like that. We can use that. And we will say, should it be roll equals tab? Maybe. Yeah, probably. What is it? Yeah, it's roll equals tab. Otherwise it would be the attribute that would happen. You're quite right, sir. You are quite right. And it's, by the way, a feature I think is very nice of seeing it. And you can select by attribute or even attribute value very underused and probably could save a few classes here and there in terms of JavaScript because you'll need to set classes. Yes. Let's see what happens with that. It's time to look a little bit more like a thing. Yay. Bored and non. So I've seen a question a few times which I think I can address. Yes, this time around we are using visual code. We used it last time, too. Did we? Yeah, I don't know. I switched between it from Atom to Visual Studio Code last time. I'm just fairly relaxed about which editor I use. If you press keys and those characters appear, that's all you need, really. Yeah, I think the only things I need, typically in my editor, are ESLint, which is brilliant. And I need syntax highlighting for ES 2015, 2016 stuff and a fair understanding of what that's going to imply. And then I think beyond that, I don't really use much else. So I know a lot of people need things like auto prefix or another things as well, which is exactly what Totally Tooling Tips is for. Sideplugged was the other champs, Addy and Matt. They do a whole show on the kind of totally things. And things you can add to your editors. Me, I'm keeping it simple. Because I am a man who likes to keep things very simple. So don't say anything, Sir Ma. I know you're fighting the ex. There you go. All right, OK. So this is kind of starting to add up a little bit better. Right, let's have a look at the SC pane content. Are you zoomed out on the main panel, though? It's just the text tiny. Sorry, in the browser. In the browser. Because I assume the buttons are probably going to be hard to read on the screen. So what I'll do is I'll bump all that up. I was just trying to get there. Just trying to get something down. I'm just thinking about our audience. A team on the chat. Hello, team. Hello, team. If you have questions, write them in the chat or Twitter or any way you think you want to contact. Send me an email. Might be a little too late. Yeah, he's not going to check his email. Use the chat. The chat is better. And I'll see if I can incorporate it here and distract him even more. I'm getting better at that. No, you're not. Well, it depends on whether you consider you're getting better at it because you're good at doing. Yeah, I'm better at distracting you. Thanks, Bill. Good to know you've got my back. Right, now you see we've got this overflow hidden. And so that's going to stop me from actually scrolling the content. And actually, anyway, these panels are stacking up. So meh, we've got to fix all that. Right, so let's do that. I think I'm going to jump that up to 48 pixels. I'm going to do that. I'm going to say font size, I'll say background, and then color. I'm going to add that white color. Hopefully that's OK. Right, I think we're starting to get to the point where we can actually add in some JavaScript, I think. Right, which is good. This is good. Kind of like into the juicy part now. Yeah, so in the event that, for example, let's just double check that that's doing its thing. In the event that settings, disable JavaScript, we don't have JavaScript. The content's starting to just stack up, OK? Pro tip, you can get the panel by pressing F1. Every time there's always something new to learn. Do you can do this? Got a new tip? You're doing a performance thing. OK, all right. So with the JavaScript disabled, you see the content stacked up with it enabled. We're starting to actually add on and enhance. So let's now actually get into some more stuff to do with that. So let's make our SCAccordian.js. So JSE, which is my shortcut here. I know it's so good for me. So SCAccordian. And we'll map that to SCAccordian. And that's all you need for the custom element, by the way. These fuel lines are a class that derives from HTML element and a register element call. And you're done. And I answered this question in the chat, but I will say it again. There's a polyfill that is like 4.5 kilobytes GZIP that basically allows you to do this kind of JavaScript coding even in IE 9 or 8, I want to say, very far back. It's a very tiny polyfill and basically gives you a very uniform platform to work with to do custom elements. It's great. I love it because I get a lot of the things that come. Normally when you do this kind of code before this, you pass in some kind of target element. And you'd always be like this.target.querySelector or whatever. Now you just say this.querySelector. It kind of merges in the JavaScript. Yeah, and you get the lifecycle callbacks and all these kind of things. It's very clean. So one of the things I feel like I want to do here. Let's see. What do we want to do? I think what we want to do is we want to do a little bit of figuring out where the elements need to be on screen. So for example, if an element is expanded, all the other panels should be either above or below. So we kind of need to. So actually what we're going to have to do is we're going to have to have some. We probably need a list of all the pains that are inside the code anyway, right? Yeah. So this.querySelector, let's start with that, I guess. And so scPain would be the thing. This.pains, why not? So we're going to grab the pains. When we attach the pains, let's say we want to do this.calculate geometries. That's a really nerdy way to call that, isn't it? Whatever. Let's stick with it for now. And yeah, it is more descriptive than bounding client writes. Yeah, that's a funny name for something. OK, here, what we want to do is we want to say this.pains for each. Just a quick question. QuerySelector all in created, but not in attached. Can that not work? Like, is there a scenario? Because in created, it's not attached yet, right? It's a node list. So it should be there. Do you want to find out? Let's find out. Let's find out. Let's find out. It will work if it's in the document, but if you create according dynamically, you create an accordion, then attach it to the DOM, or then attach the pains, and then attach them. All right, let's move it. Let's stop pains. Let's just set it up there. I like it. OK, and then we'll just get back here in the detached. OK, that's also going to work. Right, so in the calculated geometries, we're going to say pain index. So if, oh dear, all right, let's think about what we want to do here. If the pain get attribute area expanded. So I'm going to put this expanded attribute on the pain that is expanded. Yeah. OK, so I'm going to start with the first pain expanded. All right, so that's what I'm doing. If it's expanded, we're going to have to. Now, what do we need to know? We need to know the height of. Right, so let's see. This dot header height equals this dot pains 0, right? Now we need an early escape. If this dot pains dot length is 0, just return. We can't do anything here. Right, so we're going to ask for the first pains. Let's see. So we could actually, oh, let's do this. OK, so we're jumping a lot today. SC pain, pain dot JS. Oh, HS, that's not going to do it. Rename. All right, so what I want here is I want an SC pain. And this is going to apply to all the pains inside of the accordion. OK. So here's a question. Why did you call that attribute area expanded and not data expanded? It's a proper area attribute that a screen reader would understand, I understand. At least apparently in the context of a tab list. Yes, or a tab list or an accordion. I think it tells the screen reader. I think you might need to use area hidden for the other ones possibly. We don't quite know. We're working on this in the same way as we work on our performance stuff when we try and understand that, but same goes for the security, accessibility, even just how to structure code. We're all a journey in it. All the spaces to be explored. Wait, what? All spaces? I thought you said tabs versus spaces. I thought you were getting into that. I'll be the panel. Quick quiz on the chat, tabs or spaces, which is better. Oh yeah, vote, go. OK. While they're doing that, I'm going to be over here. So what I think we want to do is let's say we want, I believe those where they are for now. I'm going to say get header. And I'm going to say if this dot header, this dot header equals this dot query selector button, roll equals tab. And then I'm going to say return this header. And in fact, while I'm here, because I'm almost certain I'm going to need it, content. OK. We're getting there. Singing while you code, that's what you should do. So apparently, currently, space is leading with six votes versus seven votes versus tabs with zero votes. So we obviously have a web audience right here. We obviously have a consistent audience, which is, you know. Good. Yeah, no fights yet. That's good. We shouldn't have done that. No settings. All right. So we're going to ask for the first pane. We're going to actually ask for its header. And then we're going to ask for the offset height. So you can see what I've done here is I've asked for this header, which is going to call into the getter here, which is going to query selector into the to find the button. So it's just a kind of way of just providing a little bit of an interface to that header element that should be inside the pane. But we need to make sure now that we actually have those two scripts, source equals. We'll do the pane first because. So if you register an element, will the browser go and update all the existing ones immediately? I think, yes, I believe so. I think they start off life as an HTML unknown element. This will be, again, this will be a question for someone like Eric Bileman. True. Or Rob Dodson, again, who both are very, very, very knowledgeable about these things. But my understanding is it starts off life. The browser is going to see SC pane and SC accordion go an unknown HTML unknown element until the JavaScript kicks in. And we're calling document.register element scpane telling it what it is. And it goes, oh, cool. Let's do the work. Let's do all that work now. And unknown element basically behaves roughly like a div, right? It's just a span, I think. A span. I think it's more like a span because I don't think it's display block. I think it's more. But it's inline. I think it's inline. So it's more like a span than a div, I think. OK, so we can go. So we know the header height. Let's just say, let's do this. OK, now let's leave it where it is for now, because we'll see if we need it later. So we're asking for the header height, which we asked for once. Let me just see if that works. So we go to there. We'll run the debugger. And local header height is 48, which is correct. Because that's what I specified here in here. So that's working as intended. So what we need to do is the available height. So the available height to the contents of the accordion is this.pains. So how many pains we've got? Dot length. So yeah, that's that. Times by the header height. And then we're going to actually ask for this. Good old basic math for the rescue. If I'm any good at it. So it's the overall height of the element. Let's drop another debugger statement in, actually, and just see about that. Go again. Also, by the way, a nice feature I just recently learned. When you just type debugger, semi-colon, in your code, Chrome will just stop and jump into the inspector, into the JavaScript debugging mode, even though you didn't even have it enabled. I think it's a great feature. Why, why, why, why is that not showing up? I did save it, right? So I should have available height. Weird. It's not showing. But it's not showing. It's not updating my code yet. I wonder whether I just need to do that. How about disabling cache or something? Maybe. Let's try that, disable cache. Yes, what do I make? Weird. So the available height is 456 pixels because we've got three panels, each of which has that header, one, two, three. And then the available height is going to be the 456 pixels. So what we want to do is we kind of want to set the body content of each of those elements to be that 456 pixels. It's a very strange thing that I probably look like I'm doing here, but it should all work out in the end. So in which case, I'll tell you what we want to do is we're going to say that with that, and we'll say overflow, what, overflows? Whatever that is. Overflow, the flow. Why, scroll. OK, so we can scroll that. Because what we're going to define is, I think, this long one here, it's going to have its height truncated, and then it's going to want to scroll that inside. So yeah, that should be the right thing there. So what we want to do is let's start this here. We're going to say, so what we'd do is we'd say pain, whoa, Paul, pain.style.top, no. No. Translate. How was it, by the way? Transform. Transform. OK, OK, OK. Translate. It's because I'm thinking these things through or something. This dot header height times by the index. And then a pixel thing on the end. So for that to work, the accordion itself is going to have to be position relative. Relative. And the pains are going to have to be position absolute inside that. Oh, you're going to say all position at the top and then only work with transforms. There we go. There we go. So panel one, panel two. Now I wonder why. Why, why? Let's have a look. I was expecting all three to be stacked up there. One. And then panel two. And then why is that panel three down there? Yep. It's fine now. Go on. Do you have an idea? That's a no from Serma. Wait, did you have the pixels outside the parentheses? Probably. Yes. That would be why. Translate, why? Does that work? Does it make a difference? Oh, do you know why? Because you want them to be top zero. Probably. I even said that. Now they're all overlapping. Yes, now transform, translate, why? So that should be, is that top zero? This is always the fun bit, is, yeah. So why is that pane, SC pane? Does it have position top zero, though? At the top zero? Well, no, that's what I was wondering. Is why, oh, it's because I put that on the button. On the button. Hey, Paul. Right. All panes, all pane, should be like so. But only when enhanced, right? Yeah, OK. You're right. So we can do that. OK. Now that's disabid. Well, that's not what I expected. Ship it. Yeah, it's perfect. OK, max height. So let's just change it to height 600. Oh, yeah, of course, it doesn't have height anymore. Yeah. It's pretty. Now it looks really, really broken. So what we'll do is we will also say with 100. Let's see if that helps. It should. Yeah, there we go. OK. So one, two, and three are doing their thing, which is good. Don't worry too much about the overlapping text. No, no, no, we're going to get to that. Don't worry, we're going to get to it. So that is all looking OK, I think. Let's see. So we've got the first panel is there, second panel is there, and the third panel is there. So that's good. Now we need to make sure that each of the accordion height is OK. So then we say pain dot. I was right, I need the content. So I made another getter here for the content. You can see that here where I'm just asking for the content. So pain.content.style.height equals this dot available height in pixels like so. So that would then mean that these should all get truncated down, which is fine. Now what we need to do is we need to basically, and rather than this is the offset for each one, but we need to kind of bump the non-expanded ones out of the way. So this one, panel one is expanded, but its content is hidden by panel two and panel three. So we don't want them to be moved down. Yes, we want to basically move them all down by the available height. So that's where this comes in handy. We can say, so OK, I think what we're probably going to do is we'll say let base y equals 0, and we'll do base y plus that. So by rights, that wouldn't change anything. But if you hit the one that's expanded, base y becomes the available height. That didn't work. Why did that not work? We have bugs. Who's? Let's go through step by step. OK, area expanded, get attribute. OK, let's go through step by step. So none of them think they've got this area expanded attribute, which is unexpected since I think. Is the function running at all? Oh yeah, it should be like the other trends. Yeah, yeah, yeah, exactly, right, exactly. So let's just, I'm just going to write it. Actually, I'll just do that. Yeah, so they're going to be here. That is working. SCPain, you seem to be there. So what you can do is what you can do like $0.getAttribute. Actually, you can just do pain, can't you? Because in scope, getAttribute, area expanded. So he says no. I know why. I remember it from Rob Dodson's talk, where he said, the area attributes are really like maybe we actually explicitly need to set equals true in the markup. Otherwise, the browser will just ignore it. Cool. All right, area expanded equals true. Do you see if that works? Yeah, no, wait. Resume. There we go. There we go. Nice work. He did something. Nice work. And also, well done, Rob Dodson for putting out the. Thanks for teaching me. The college, well done. Right. So with that done, interesting. So now the panel one is expanded. Panels two and three are out of the way. It feels so real. So this is starting to feel like something we'd want to actually say is working. So let's do interaction. No? There we go. Just a bit of a divider line just between those panels just feels a little better. How's everybody doing on the chat? They're saying they're learning things. They're learning things. We're achieving our goal, man. Yay. I learned somebody said add a border bottom between each button. Yeah, I did that. And we came up with the. Yeah, it's good. OK. All right, so here's what I also want to do as well. As I want to add, I'm going to add a focus state here as well to the button because I think it's helpful to know not only that the panel's expanded, but you can see that slightly. The default for the browser is to put this usually blue-ish outline below around it. But I suppose what I'm doing here is I'm just sort of doing that to it just so that it's very clear which of the panels is hopefully going to have focus in a moment. And you can see also I'm actually getting tab focus here as well, which is good. That's what we want. Right. So now the question becomes, how do we actually then animate these things? Seems like a thing we want to do. Let me call Paul Lewis. Animate transforms and not positions. Wow, it's like having another me stood just to the side. OK, good. What I think we want to do, let's just say, let's do that. And we're going to say move panels. I'm doing this. What I'm doing is I'm moving the bit that moved the panels out to its own function because I think we're going to want to do that in a mode. Let's do that. OK, that's the works. Here's something. You see that little flash when I first refresh? I don't know if you saw this. No, it's not doing it now. It did it just a moment ago. There. There. Yeah, that's because your progressive enhancement is starting late, right? Yeah, so exactly. So we should probably account for that and just say if we've managed to set the attribute of enhanced, OK, we should probably actually set display to none. And then what we'll do is when we get something where it says, say something like active, we'll set display to block. Now, you might just say visibility hidden here, actually. I think I might do that today. Visibility hidden and then visibility visible. Because we don't want that flash. And so now what we have to do is, oh, actually, and I can take advantage of this as well. Well, let's just one thing at a time, Paul. One thing at a time. Breathe. When we've attached it, we're going to say this.classlist.add. No, it's not a class. Set attribute. There we go. Attribute. It was active. So when we see that now all being well, we should never see that flash. It's going to be invisible, but it'll still take up its layout so we won't cause a reflow or anything like that. Right, that's good. Actually, interesting question. Somebody's asking why visibility and not opacity 0. Yeah, you could do opacity 0. And if this was a primed element, so I talk about this bunch in my side nav. Yeah, the side nav one. And I talked about it at the progressive web app somewhere in Amsterdam. If you've got an element that is primed as in you're expecting this thing to be on screen immediately or could be on screen, then using opacity 0 and will change transform stuff is like the right call. So the difference is now that when we are switching from non-active to active, the browser is actually to add it to the DOM and render it. While with opacity, it would already be rendered. Yeah, so you're going to paint it anyway. So in this case, we see that when I switch on paint flashing there, you can see that. You can see that it flashes green on screen there. Because it's going to paint it either way. Whether we do the opacity 0 and then fit in. It's a large part of this is basically going to depend on the app that you're building. And also how long you think it's going to take for those definitions to arrive for those custom elements. If you think it's going to take a while for them to arrive, you might be better just switching the whole thing off. Bearing in mind that I actually have some styles that are, in this case, if I use opacity 0, I'd still have style changes to make as in paint changes that would happen when the definitions came in. So even if it's opacity 0, you'd still have to repaint it and then fade it in. So I'm just toggling the visibility. There's loads of ways you could play this. OK, right, so now what we need to actually do is when you click on one of those buttons, we want it to kind of shift. So let's see what we can do there. Created callback, that's actually a pretty good thing. Let's do this. What we'll do, we want attached, I think. We'll do attached callback, attached callback. Yes, you do call. Yes, I do. OK, this.header.devent listener. Click. Another thing that I was taught by Rob Dodson in his Iowa talk, I think, is that because we're using a button for our header and not something else like an H1, we will get keyboard handling for free. So the click handler will fire even though somebody is pressing the Enter key on it. This is good. Which is something you would have to do manually if you weren't using a button. I'm going to do a custom event because I think that's quite good fun. So there's the type arg, which is a string. So what are we going to call it? We'll call it panel change. And then we need to tell it that it bubbles up. So it's going to happen on one of the panes and we're going to have the accordion catch it. It's going to be going to delegate handling out to the accordion itself. So we'll say bubbles. Bubbles. Can't even type bubbles. Bubbles. So we're going to say this bubbles up. Custom events, nice. And Linting is not happy with me. Go, go. And we don't need that event, actually. So we'll just do custom event. And then there's a dispatch event. I think so. Dispatch event. Not this, though? No, it should be window. Or maybe it's this. Let's try this. It needs to bubble up from somewhere, right? Yeah, yeah, yeah, right. Dispatch event, custom event. Hopefully that will work. Let's find out what we'll do is we will have panel change. So let's do this.add event listeners. No underscore, though. Oh, oh, you're doing the whole batch. I didn't say anything. I was wrong. I'm not keeping score. So this.add event listener panel change. And then say this.onPanelChange. OK. So on the panel change, we'll do just a bind overwrite. We'll see. I probably do, actually. I probably probably do a good call. So we're going to just add this event listener. And we'd probably need to remove event listeners. But let's just do this. So when we do that and we click, we're firing the debugger. Nice. So we actually just sent a custom event that didn't exist before on the platform. And we made it. So we made our own customer event over here called panel change. We said that it would bubble up from the pane all the way through to the upwards. And we just kind of intercept that at the accordion level by adding an event listener for panel change. And that is going to work out really well for us. So now we know. Let's see. So we can have a look at, in fact, back here. We will have. So of course, yeah, there isn't any. So when we do, we can just say, let's put the debugger statement back in. So I click on one of these. And the event is the custom event. And the target for that is the pane itself, which is actually very, very useful. Because, so we can say event.target.set attribute area expanded to true. Like so. Maybe you have to do it like that. Probably have to do it like that. So we set that. And we can say, let's say this.query selector all. Actually, we can just do this.panes. We've already got them. This.panes for each. And we'll say pane. I just want to say these for each map and reduce are my most favorite things about ES2015. They're so good. It's so good. This filter, I really like. Filter, find, map, reduce. Once you get to use them, I find I had to use them quite a lot before I kind of, they clicked. I don't want to miss them anymore. Map is map. Which one's map again? OK. Eventually, yeah, I got them. For me, I don't want to go back. Right, so for each pane, we're going to say remove attribute area expanded. So are you just removing it from all of the elements and just putting it onto the one? Onto the one that whichever one was actually clicked. And then after that, we can say request an animation frame. We want to wait because we're going to make some visual changes to the transform here. So request animation frame. And then we can just say this.move panels. So we're doing a request animation frame because we basically need to wait for the browser to be aware of the change of the attribute, more or less. So we might work out fine, but I feel like we're making a visual change. And I think I always just want a default to, I'm making a visual change that should be in a request animation frame if I'm having multiple things. That's probably a good habit to just have. Yeah, it's a habitual thing for me at this particular point. All being well, something would happen, but I don't know that it did. So let's have a look at why. So we're tapping on this. So area expanded true, area expanded true, area expanded true. So it is swapping the area expanded. And too, what we can also do while we're here is we can also say we could do that attribute hidden true. So we could say that these panels are definitely hidden. And then the one that we want to work with, let's just say, const target equals that. We will say target that, and then targets that. And then I still wonder why it is. Got to spell debugger correctly for this to work. OK, that's working there. OK, it's not firing. We have an error, though. We have an error. Pain.addAttribute, setAttribute is what it really should be. So that's that. OK, so we'll go back there, fire you. OK, so that's not firing. That's interesting. Unexpected. So this.movePanels is not. You should probably put parentheses behind movePanels. I should make it a call and not just a reference to a function. That'll be what it is, folks. Yeah, got the debugger statement. Well done, dude. There you go. All right, let's see what happens now. I think that also looked like it went wrong. Yeah, I think so. Let's find out. So panel two. Yeah, so panel two. So panel two, panel two. So that one didn't actually push down where we expected it to do. Wait, there's two? Oh, it's just basically it's sitting over the top of. So we've got one, two. We expect that translate wide there. Not to be 96 weeks, which would be like 500 or something. Yeah. So for whatever reason, that getAttribute didn't actually. That's because I'm here. Because I actually did that. Right, let's try that again. There you go. There we go. There we go. Panel two. So this is already really, really close to what we were wanting to achieve. So now the last thing that I just kind of need to do here is just to set the background color to be white. Because you can see here the panel three. They're overlapping. Yeah, they're overlapping. So that's good. So if you want that visual effect, go for it. So you can see here, I can actually tab as well. So that's actually doing its job, which is cool. What we could also do, let's see. Oh, yeah. The major thing is not actually animating. I was about to stop you and be like, Paul, we are talking about UI and animations. This is me. Just to fall, you idiot. Right. So if you want to be active, when the accordion is active, we know that we've, in theory, got everything set up. Well, I'd say that. Let's go and have a look at the code. When it's attached, we do all the blah, blah, blah, and we move the panels. And then we set this attribute to active. What I'm going to do is I'm actually going to hide that behind the request animation frame. Because what I want to happen, in fact, I'm going to end up having to do this, do it twice. But we'll find out what's going to happen is we're going to make all these changes, particularly moving the panels with the transform. I'm about to switch on transitions on transforms. But I want them moved first so that we don't see an initial slide of the page. In fact, let me just show you. So that means we're going to basically put the animation, the transition, only when active has been set up. When active, yeah. I'm going to do that now just so that you can see what I mean. So when the accordion is active, we're going to tell an SC pane that a pave that we'd like it to transition on transform. And we'd like to take 0.3 seconds. Come on, do the QBB there. You like it? Yeah, 0.31. So you see, when I first do that there. On load, we already have an animation which we got one. We get that initial, which just looks just smacks of, oh, I quickly got this together. I don't know. It just doesn't feel right, which is why what I was saying was what I'd like to move them first and then switch on the animation. So we can hopefully do that by just hiding it behind here. So that means move panels will be called, we'll take care of it positioning the panels. Yes. And then we will add transitions. So now you can see we've actually got a functioning accordion. We can scroll up and down in between the panels. We can do so. We can tap around. What I actually feel like I wanted to do that is I wanted to press up and down, up and down, left and right on the keyboards. And I think that's something we could add hopefully pretty quickly here. Let's give it a try. Let's see. We have a question on the side why we use overflow Y scroll and auto. And I think auto would be the better choice actually. Well, it depends. So overflow Y scroll is going to put the scroll bar there the whole time. So you won't see it here when I've got a track pad. So here you won't see it on the track pad. You only see it when I actually scroll and then it fades away and disappears. On a device where you've got a mouse plugged in, often you'll just see the scroll bar just there when it's overflow Y scroll. That's what you have right now, right? Yeah, but it will even appear for these. So if you want the kind of visual consistency of all my panels have scroll bars, whether or not they need them, you'd have overflow Y scroll. If you're happy for it to just only appear on demand, that's when you use auto. So it's a valid question. It's about a choice to make both work and I guess, yeah. Yeah, both work. In my situation, I feel like I just want the scroll bar there to be like, this just has a scroll bar. If you had a chat with your designer, if you are a designer and you're like, no, no, no, scroll bars are only there on demand. Okay, well overflow Y auto for you and there's no problem there. Whatever makes sense for the thing you're building as usual. Right. So with that said, I was saying I wanted to press kind of up and down because we're doing fine. We have time to spare. So we can do this dot add event listener. Listener key down. I would go with key down. Yeah. Key down. You can do on input. Input is a very strange one. But I think input is only defined on input elements. Nope. Yeah, maybe. Probably. Okay. All right. How are you? How are you with your, your, your key codes? Um, I, I always just console them and press the keys that I need. Yeah. Which one is this? Actually, I mean, we have add in the newer API is we actually have string identifier. So you can say key underscore a or key underscore arrow up or whatever. But even then that is not very well supported. And I still don't remember the name. So just, just yeah. Okay. Let's do that. Yeah. Yeah. That way console.log ebt.keycode. I think this is, this is how I would normally do it anyway. Oh, just be like, okay, I need to know. You end up building the switch case anyway. Key down. That didn't work. Hang on. Uh, set focus on one of the tabs. Maybe it's, maybe it's, maybe it's that. Is the accordion the thing? It tapped one of the, no. Okay. That's what it is. Didn't have focus, right? So up is 38. Down is 40. I hope everybody's paying attention. Remember those pop quiz tomorrow. No pop quiz in about 10 seconds. When I go, what was up? Up is 38. Down is 40. Left is 37. And right is 39. Up 38. Okay. So it's basically starting 37, 38. With those, we want those. It's starting left and going clockwise. That makes no sense whatsoever. Don't worry about that. Switch. We're going to switch. And if we see switch and we see case 37. Or case 38. And if you ever write a switch case, do people have ever put a comment next to them? What keys those are? See, I was going to do them. Or do fancy getter things. Yeah. So key up 38. Um, key left 37. Why not? Okay. Um, key right. Key down. Can you remember? Anybody remember what key right was? Somebody said up is 63. And I think they're wrong. That's not true. Don't, don't, don't make life hard of us. We already have parts on our own. Right was 39. It's left is 37 and then clockwise. That's, okay. Right. So case. Um, you and your logic. Hang on. Let's do that. Start it. Folks, you don't know, but it is incredibly warm in this room. It's very, very, very warm right now. There's the things we go through for you. It's, it's a, we've got a lot of love in this room right now. Key up. All right. Let's do a key up. So this makes it so much more easy. Easy. Easy. Key up or key left. And then that we have added this to the platform. There is the, I think it's called dot key, which is a string. And then if I, which just says escape or key up, but it's not widely supported. And I don't remember what the strings are as well. Yeah. I can't remember either. Oh, is it complaining? Cause it doesn't have a default expected default case. Well, okay. Then fine default. Nothing. Break. Do nothing. Uh, so key right and down. Okay. So what we want to do first of all is like, we need to figure out which one is the currently expanded item. So we can do this dot query selector. And we can say SC pane, which is area expanded. Yeah. That would, that would be the thing. And what we'd want to do is we say, so we'll do let index equals thinking, thinking, thinking, I'm thinking. Find an underscore paints or something. Yeah. I can't because it's, it's a, I can do, okay. Array dot from. So we have to convert the node list over to a thing. And then we just say index of that. That's totally what I meant. That's, is that really what you meant? Roughly. Okay. So fine somehow returned an index, but maybe it doesn't. So then what we want to do is we would want to say index minus minus index plus plus. That's all we want to do there. And then I think outside of that we want to say if index is less than zero index equals zero. Else if index is greater than or equal to this dot paints. How's everybody doing on the chat? Pretty good. Yeah. Some people say they are sitting in rooms just as hard as ours. I doubt it. I'm actually melting. Imagine if I had hair. I mean, how are you coping? I wouldn't look ridiculous. I would look. I actually looked ridiculous with hair. If that were possible, I looked more ridiculous with hair. Anyway, so if we hit the end of the array, we're just going to set that to this dot paints dot length minus one. And then what we want to do, and then what we want to do, you know what we're going to do? We're going to have to find that again. So let's just do const. Why is that invented like that? That's not going to work. Oh, it is. It's in there. It's fine. Right. Fine. The editor is right. How dare it? OK. Paints array. Or whatever. It'll be fine. Apparently there's a dot find index function on arrays now. There is. There is. There is. It doesn't return the index, though. It returns the item itself that it found. So you'd be like, like, paints array would be, I thought, no. It'd be like, let found item equals paints array. And then you do dot find. Then you give it like, and then whatever criterion you want, you do dot. But it's also apparently find index. Oh. Another function that gives you the index. Oh. I apologize to whomever said that. Do you want to check that out on? Yes. I'll quickly give it a check. Just see if it's actually in. Yeah. That's the one. Yeah. Look at that. That's OK. So find index. So what's that going to return? What? You give it a callback. You give it a callback. I'm just going to leave it as is. It's not going to be broken. We're not going to fix it. But thank you for teaching me something. Yeah, and me. I appreciate that. We're all learning things here today. This is why we do the show. We're going to find the one that's expanded. We're going to find its index. We're going to increment the index. We're going to lock it so it doesn't go out either side. And now we want to find the new one is going to be panzerayindex. Right? This is the new one. And then what we want to do is we want to call this dot onPanelChange. And we want to do that with it. But PanelChange takes an event. I know. I know. Well, it takes something with a target, actually. That's true. So we can just pretend to be an event. And just make it work. Yeah. Like that. So all being well, that's strange, isn't it? Well, the focus still sits. You have to call, actively call dot focus, I guess. Yeah, that's right. You're right. I don't want to do that. I actually just want to call. You're right. I just want to say panzeray. Dot click could actually work. Dot header. Focus. Yeah. No, I want to find that. Oh, yo. Right. I'm just in theory. Oh, that's interesting. Why is it doing that? If it is greater than or equal to the panes dot length, should have been OK with that. Hang on. Let's just. Interesting. Well, let me go past 1. So it's not happy. It was a panes dot length. Huh. Interesting. But that should be index 0, 1, 2. If it's that. Interesting. Oh, of course. Oh. I don't get it. I don't. Right. You're ready for it? Yes, unlike me. Actually, this is going to be messy. Let's think about a better way. OK, so index is basically being reset each time. It starts from the currently expanded item. We don't necessarily want that. Dot on the selected item. Yeah. So what we want to do is on the panel change. Can you just query for dot selected or dot focus? A colon focus? Focus. Yes. You're right. You could do the scpn dot. Yeah. Column focus? Yeah. Shouldn't that work? Yeah, that's kind of messy. Or get focus. Isn't there like active elements on document or something? Yeah. You're right. You're right. You're right. But then you'd have to find the containing parent and then jump to the next parent. I mean, you could, but you're now very brittle. Because if that structure of your DOM changes, for some reason, you're going to be in a funny, funny place. Also, good remark. Somebody said we should just do modulo there. Yeah. You can do that. Definitely can do that. But you'd be wrapping around. Oh, that's true. We don't want to wrap around. We don't necessarily want to wrap around. We might want to just lock to the end or lock to the start. So modulo, definitely if you want to cycle around. So let's get longer. It might actually be good to wrap around. But for now, we're going to keep it this way. Yeah. So I feel like, yeah, you're right. What we can do is we can at least ask for, let's see. Button roll tab. OK. I mean, we could do SC pane. Sorry. I'm just debating this now. OK. Roll equals tab colon focus should work, right? We can do that. But I just want to make sure. OK. Sorry. Which way do you want to do this? I would just query for SC pane space, the roll equals tab colon focus. OK. But you just do star, roll equals tab. You don't have to do anything, I think. You just do roll equals tab. I think it works. I think I've done this before. Shall we find out? Right. So you'd, hang on. You've got this here, const selected item equals that. And that's going to focus. Yeah. Focus or focused? Focus. Yeah, should be right. Focus, right? Yeah. I'm just looking at the stars. I don't know if you used it there. OK. So with that, selected item is the button. That's true. Which is great. That worked. So then we can say selected item dot parent node. Right? Yeah. OK. See if that works. You're joining a button and joining an array right now. Am I? Aren't you? No. Oh, it nicks off. Yeah. I'm stupid. So basically, so we're finding the currently focused header. I feel like that's brittle, but OK. We could probably do better, but for now. Yeah, you're right. This is prototype land. Paul, prototype land. Live in the prototype. OK. So we're going to let the index be that. We're going to lock it in. We're going to basically, if it goes past the end of the window, which is fine. We're going to do that. There we go. I think. I think, sir. Fairly confident. We are working as intended. Look at that. And if the person was pointing out that you could do that. So you could just do index modulo equals this.pains.length. This would be another way to do it so that you basically cycle around like that. Yeah. Which would be fairly useful if you have like a lot of items. Yeah. And actually, let's just see if that works now. Let's just, what we can do is let's add in a bunch of other items here. Well, we're just going to add in one more because this, yeah, I don't want to. Oh, living on the edge, aren't you? No, no, I'm really not. Not today. All right. We are running long time. It's working fine with multiple panels, multiple panes. And hopefully, folks have enjoyed seeing us work through some of those fun bits and pieces. Right. We did something. We did. We did. We did. We'll post the code as usual. Thank you so much for joining us. Don't forget you can subscribe. So we're now going to take a bit of a break now over the summer period. Because it is just too warm in here. It is too warm. Enjoy the sun, which is not becoming for the typical developer I fear. But whatever. I mean, he likes the sun. I don't like the sun. Together we'll probably be making up. Probably average. Anyway. Yeah, thank you very much for joining us. And we will catch you probably later in the year. See you then. Bye.