 Hey, everybody. What's up? It's Rob Dodson. Welcome back to the Alicast show. Today, by popular demand, I am going to build an element end to end. We're going to be building a toggle button based on some work a developer named Rajat did recently on our how-to components project. So we're going to walk through the implementation, and then we'll use this as an opportunity to talk a little bit more about the how-to components a little bit later. But follow me over here to my laptop. You can see this is what we're going to build today. It's a very, very simple control. Toggle button, you can click on it, and it has sort of like a permanent pressed state. And you can click on it again. Unpresses. Pretty straightforward, right? Now, anytime I'm building a custom control, the first thing I do is I usually go over to the ARIA authoring practices site, and I'll try and see if there are some instructions for the pattern that I'm trying to implement. So over here on the site, if I zoom in a little bit, we can see that there is a button section. And it tells us that a button is a widget that lets a user trigger an action. But it also says, in addition to the ordinary button widget, ARIA also supports two other types of buttons. There's a toggle button, and there's a menu button. A toggle button is going to be like a two state button, which will either be pressed or not pressed. And kind of like the quintessential example they provide there is like a mute button that you might see on a video player. The important thing to note here is that they mention, in order to tell a sense of technology that a button has a toggle button, we want to specify a value for the ARIA pressed attribute. So right away, we know we're going to need an ARIA pressed attribute. If we look at the instructions, we can also see that there's some keyboard support we need. So it being a button, we need to support the space bar. We need to support the Enter key. We can skip some of this other stuff, which talks about what if your button opens a dialog and things like that. We're not going to worry about that too much today. But we can go down to this section on roll states and properties and see that, OK, since we're building a button, we need a roll up button. Got it. We don't necessarily need to worry about having a label on this button, because buttons, as it explains here, can get their accessible name by the computed text content that they have. So if we actually have some text inside of our button tag that says, click me, or mute, or whatever, it'll just use that as the label for the control. Otherwise, if we were building a button that only had an icon on it, we might want to add an ARIA label to that. But in our case, we're going to just make a standard actual button, so we can just consume that text. It'll also tell us that we need to support the ARIA disabled state. So if the action or the button itself is disabled, we've got to add ARIA disabled. And as it also stated earlier, we've got to make sure we support ARIA pressed. OK, so there's a number of things this control is going to need to implement. Even though it's simple, it'll actually take us a fair bit of work. And I've actually started to template this control out a little bit already. So I'm going to do this as a custom element. If you're unfamiliar with custom elements, I can leave some links down below in the show list of some of our documentation. But basically, a custom element just you define your own HTML tag. So in this case, we're going to create an element called how to toggle button. Now over here in my template, I've gone ahead. I've started to stamp this template out a little bit. So I've got some key code constants that I've defined for the space bar and the integer key. And if we scroll down here a little bit, here I'll expand this so you can see it a bit better. The first thing we're going to do is we're going to create a template for our elements implementation. And what we can do inside of this template is we can create some very basic initial styles for our element. We can also add a slot tag to this element, and that's actually how we'll consume the text content from our element and display it properly. So inside of my template, what I'm doing here is I've just called document create element, creating an actual template tag. I'm setting its inner HTML to this little string here. And I've got a style tag and I've got a slot tag. My style tag, there's a few styles that I want to set. So this colon host selector right here, that allows us to style the custom element itself. Self-applying styles there. By default, all custom elements are display inline, but since we're building an actual button, we probably want it to be display inline block. We can do some other things too. We can give it kind of like an initial border. We can say it's one pixel solid black or something like that. We don't want it to be too stylized. We can give it some padding. Say we want it to be like three pixels. If we want someone to be able to style this control later, we could use CSS variables, a.k.a. custom properties. So we could say var dash dash toggle border. It'll either use a variable if one was defined, or it'll just use the shorthand as a fallback. So we can do something like that. That's kind of cool. And then there's two states we want to support for our button. There is a pressed state. So this style selector here signifies, if on the host we have a pressed attribute, then we should apply a style. And then this one right here says, if on the host we have a disabled attribute, we should apply a different style. So all I'm going to do in this case is just sort of change the background color on our button so we can verify that it's actually working. You could do something fancier with your control, if you want. But we'll keep this one pretty simple. So when it's in the pressed state, we'll just set the background to light green. And when it's in the disabled state, we'll set the background to light gray. The next thing we're going to do is we're going to have a little class here that we define our button implementation inside of. And then finally, we call custom elements define. We pass in our tag name. And then we pass in our toggle button class. OK, cool. So now we get it to kind of the heart of the matter. Let's start defining this class and all the things we're going to need to support all those ARIA semantics. Now, when I'm building a custom element, usually the first thing I do is I define the attributes that that element will support. And as we know from these selectors that we put in our template earlier, we're going to need a pressed attribute and a disabled attribute. So to do that, we create a static getter. Observed attributes is its name. Observed attributes. And we want that to return an array of attributes that this element will watch for changes on. So we'll say we want to watch for the pressed attribute and the disabled attribute. So now anytime those change, we can actually implement a callback called attribute change callback, and we'll get those signals. Our next thing we want to do is we want to define a constructor for our element. So anytime you're creating a custom element, the first thing you do in your constructor is you call super. That sets up the prototype chain with HTML element. And this is a good place to go ahead and create any sort of shadow DOM for your element. Since we're going to be taking in the text content from our element and displaying it with that slot tag, we can use shadow DOM here to do that. So we'll say this dot attach shadow. We'll set its mode equal to open, which means people can style it later on. And then we can just say this dot shadow root dot pen child template dot content dot clone node true. So what that's going to do is that's going to grab all template content that we have up here and stamp it out inside of our element. The reason we do it in this fashion when we create the template element ahead of time is by doing this and cloning it into our tag and just instead of just setting inner HTML, we actually save a lot on HTML parse cost. So this is kind of like a nice little custom element trickery here. All right. So we've got our constructor set up. We've got our element and it's registered. So I think at this point, we could actually even go look at it in the browser. And we should see, yeah, so we've got our little border around this button that we've been working on. It doesn't really do much right now. We can't click on it or do anything like that. But at least we know that it's showing up. The next thing we want to do is say, OK, cool. Once this thing is connected to the document, let's go ahead and sprout some of those RE attributes. So we want to sprout a roll, a button. And we also want to add a tab index to this control so it's interactive. So we can do that inside of our connected callback. So connected callback runs any time your element gets attached to the document. And inside of here, what I often do is I say, if we do not already have a roll, then we can self-apply one. And the reason we do that is because if the user of our element has decided that they would like to override the roll with something else, maybe something fancier, we don't want to surprise the developer and override them. So we always check to see first if we do not have a roll, so this does not have attribute roll, then we can say this.setAttributeRollIsButton. We'll do the same thing for tab index. So if we don't have a tab index, then let's go ahead and set that to tab index 0. This means it'll be reachable by the keyboard. And then finally, because we're building a toggle button, we need to signal to assistive technology that this button supports those two states. So we need to go ahead and define an initial aria-press state of false. And we'll do the same thing here. We'll say, if we don't have the aria-press attribute, we don't have aria-press. So let's go ahead and define that as setAttributeAriaPressed is false. OK, so far, so good. If we go back and we look at our element now and we open up our dev tools and inspect it, we should see that it has, here, we will embig on this a little bit so you can read it easier. Whoa, that's a little too much. OK, so we can see that it has now spreaded roll. It's spreaded a tab index and it's spreaded aria-pressed false. Next thing I want to do is define these attributes and properties for pressed and disabled. Now, the pattern that I often use is I'll create getters and setters for the underlying properties. And I'll just have those synchronized with the attributes. So setting the attribute sets the property, setting the property sets the attribute, right, back and forth. So since these are Booleans, we can go ahead and we can follow a pretty simple approach here. So we'll say setPressed that will receive a value. So this is what this function runs when someone sets the property itself. And what I usually do is I will define a little constant and I'll say isPressed equals Boolean value. Whoops, let's spell that right. Boolean, there we go, value. So why do we do it this way? Well, if you look at the way that native controls work, like the input type equals checkbox, when you set its checked Boolean property, you can pass it all sorts of different funky things. You could pass it like the number one in quotation. You could pass it the word false in quotation. And since all of those things are truthy, it will go ahead and it will mark the checkbox as being checked. So we're kind of doing the same thing here. We're saying anytime someone passes something truthy to the control, we'll go ahead and indicate that, yes, it has a value. We're going to say that's a true Boolean value. So if isPressed, we will just setAttribute, pressed to empty quotes. So this is how you actually signify that, yes, you have a Boolean attribute that's true. Otherwise, we'll just remove the pressed attribute. So removeAttribute, pressed. So all we're doing here is we're reflecting the property to the attribute. And when we ask for the value from the property, so we getPressed, we're just going to return this.hasAttribute, pressed. So now these two worlds are in sync. And for disabled, I'm going to basically do the exact same thing. So I'm just going to paste this in really quick. So setDisabled and getDisabled. Exact same approach. We check to see if the value is truthy. If it is, we reflect the attribute. Otherwise, we remove the attribute. All right, so we've got properties. They set attributes. That's cool, right? But we need these missing aria semantics. And what I often do is once these attribute values are reflected to my control, that's a good time to handle any side effects over in our attribute change callback. So we'll define the attribute change callback. AttributeChangedCallback. This is going to get three arguments. The name of the attribute that changed, the old value, and the new value. And since these are both Boolean properties and Boolean attributes, we can kind of do a quick little cheat here. And so we can say, hasValue equals newVal does not equal null. And if hasValue, or actually, we can do this even faster. We can say this.setAttribute. And we'll use the little template literal string here. So we'll say aria-name hasValue. Boom. OK, so now we're saying, if someone sets pressed, it'll say aria-pressed equals whatever that value is. Someone says disabled will say aria-disabled equals whatever that value is. Now we should actually be able to go test this. So let's look at our element again. We'll select it in our DevTools. And we can see right now it has its default rule, default tab index, and aria-pressed. Let's say pressed equals true. And now we can see aria-pressed equals true, just appeared, and our pressed attribute just appeared, and our style is applied. Similarly, we could say disabled equals true. And we should sprout a disabled attribute, an aria-disabled attribute, and our style is now gray. OK, cool. So we know that at least the programmatic and the attribute API to this element works. But we still need to add click and keyboard support. So the last thing we're going to do is we're going to define a function called togglePressed. And inside of this function, we're just going to toggle the state of our pressed property. We'll say this.pressed equals the opposite of whatever this.pressed currently is. If you want, you could also dispatch an event here to tell the outside world that, hey, your element has changed or something like that. But to keep things quick, I'm going to just skip ahead. And I'm going to say, let's add some event listeners. So open our connected callback. This is a good time, I think, to add event listeners. So I'll say this.adventlistener, click this.onclick, this.adventlistener, key down, this.onkeydown. OK, now our click handler is really not going to do anything special here. So in our click handler, we could have actually just called togglePressed directly if we wanted to. I'll manually call it here. Perhaps you might want to do some other side effects when someone clicks on your control. This gives you the option if you want to. Otherwise, you can just go ahead and call togglePressed. Key down will be a little bit fancier. So we're going to need the event here. And first we'll say, if the event.altkey was held down, let's just go ahead and return. The reason I do this, and this is a little hand wavy here, but as I understand, some screen readers allow the user to hold down the alt key as a modifier key. So they might be able to hit alt, spacebar, alt, enter or something like that, I think. So I want to make sure, if that is the case, we're not intercepting those key presses because they're not meant as spacebar inner key presses. They're meant as modifier key presses. So we'll just go ahead and return those. Otherwise, we're going to switch on the event.keyCode. And if the case was keyCode.enter or if it was keyCode.space, then we're going to prevent the default action. And we're just going to call this.togglePressed. I'll break. And then for anything else, any sort of like default action, we are just going to go ahead and return control back to the program. So now if we go and we check out our control, we can click on it. We can see that, yes, the click handler is working. We see the AriaPressState toggling. We can select it with our keyboard and press spacebar or enter. We see that working as well. Or we can select the element and set disabled. Disabled equals true. And let's select the actual element when we do that. There we go. And we can see the background color changes to gray. So that about covers that toggle button implementation. There are a few little bits that I left out. And if you want, you can go check out the how-to components repo to pick up those additional sort of like in the weeds bits. But I actually wanted to use this also as an opportunity to talk about the how-to components project. So I mentioned it last time on the show. And the idea behind how-to components is we want to create kind of like a library of example custom elements that developers can look at, understand how to write these controls in an accessible fashion, and then take those implementations, take those ideas, and kind of like map them over to their own project. So how-to components are not meant as like a for production UI set or anything like that. They're meant entirely as like literate code examples. And our goal is really to take all of the Aria authoring practices guide and see if we can implement those as custom elements so that all developers can build cool web components off of those. So if you yourself are interested maybe in contributing to this project, the toggle button that I built today was actually created by a developer in the community named Rajat. If you want to do the same, you want to pitch in, that would be awesome. We would love the help. So go over to the how-to components repo. It's over on GitHub. And open up a poll request if you have the time. That about covers it for today. So as always, thank you so much for watching. Leave me comments down below if you have them. And I will see you next time.