 Hey everybody, what's up? It's Rob Dodson. Welcome back to the Alicast show. Today, I want to talk about how we label elements inside of the Shadow DOM. So this is a topic that is kind of near and dear to my heart because I do a lot of weapon components work on my other show, Polycast. And recently I had a Googler reach out to me who was trying to build some interactive controls. Wasn't quite sure how to associate them with a label element. So follow me over here to my laptop and you can see that I've gone ahead and I've set up a little example. So I've got a label element here that just says first name as the text content. And inside I've got a custom element that I've been working on called element without label. So this is a custom element with a little bit of Shadow DOM. And if we go and look at the browser, you can see that what it actually renders is just like a regular old input control. So if I go in my inspector, I can see that label there and see that element there. And if I pop that open, there's a shadow root. And then inside all I've done is I've dropped a little input tag inside of there, right? And we can go and look at the actual implementation for that control even. So element without label, this one right here. So we can see we're calling the constructor. We are attaching a Shadow DOM or a shadow root and then just pop in an input in there. So this, I mean visually, like this works, right? We've got a label and a control next to it. And someone, you know, just, you know, looking at the screen could click on that control. But let's see what happens when we try and interact with this control using something like a screen reader. So I'll turn on voiceover. Voiceover on drone, labeling Shadow DOM, went Edit Text, blank. So you can see it just says Edit Text, blank. There's nothing associating it with that label. Even though if you look at my elements inspector, I've used the for attribute and I've pointed at this, the ID for this custom element. But the problem is what we're trying to label is this input. It's actually sort of like hidden inside of the shadow boundary there. And one of the features of Shadow DOM is that it scopes IDs to the shadow root. And this is really useful for things like, you know, CSS scoping your styles, making sure they don't leak out onto the rest of the page. So if you're styling with Shadow DOM, it's an awesome feature. You're trying to associate accessible controls kind of across the shadow boundary. This becomes a bit of an impediment because our IDs are scoped, everything with labeling works on IDs. So we're kind of out of luck there, right? So there's two approaches that you can use to work around this issue. The first is you can sneak your own label into the shadow root along with the rest of your implementation. The other approach is that you can apply an ARIA label inside of the shadow root and you can just kind of like pipe through your label text. So I will show you how to do both of those starting with the first one. We're going to sneak a label into our shadow root. So back over in my code editor, I've got another element that I've been working on. So this one is called element with label. And the implementation is pretty similar to that previous element. I want to start here talking about the constructor. And I'm going through doing all the same stuff. I'm calling super. I'm attaching a shadow root. This time I'm creating a label element along with an input element. And then I'm giving my input an ID. So I'm just going to call it internal input. And then from my label element that I've created, I'm going to use its HTML4 property. This is the same as setting the for attribute. So that's sort of like the mirror there. So I'm using its HTML4 property to associate it with my inputs ID. I'm just going to put both of those inside of my shadow root. Now the trick that I'm doing here is I'm also creating a label attribute. I'm sort of exposing that to the outside world. And I'm using this feature of custom elements called the observed attributes callback here. I'm using this feature to say, hey, I observe any attribute called label. What that does, that tells the system if someone mutates or modifies the label attribute on my control, it should run the attribute change callback for that control. So I've got this little function down here that I've implemented called attribute change callback. And whenever label changes, what it's going to do is it's going to pass me the name of the attribute that changed, the old value and the new value. So the little trick I can do then is say, all right, cool. So someone set some label text on me. I can go to my little secret label element here and just set its text content equal to that new value. And because I've already created this association between my little input and that little label, these things should just play nicely. Let's go back to our index file. We will drop this element in there as well. So I'll say div. This will be element with label, right? And we're going to give it a label attribute. And let's see. So the previous one said first name. We'll have this one say last name. Save that and go back over to our app. Cool. That reloaded. So we've got first name and last name here. Now let's try these in our screen reader and compare the difference. So turn on voiceover. Voiceover on Chrome. Language in labeling shadow DOM. Edit text. Blank. Last name. Last name. Edit text. Voiceover off. There we go. So the first one just said edit text. Blank because it didn't have anything associated with it. Edit text because we have that label association in the shadow DOM there. Now sometimes you might be in a situation where you're building an interactive control and having like onscreen text doesn't necessarily kind of like work with what you're trying to do there. So an example would be like the classic hamburger menu icon button thing that you see on a lot of responsive websites. So oftentimes we don't have any like text. So we wouldn't necessarily want to use a label element per se, but we could use something like ARIA label to label that control. So I'm going to do that as sort of a second example and show you how that works. Very similar to what I did with the element with the label. This I'm going to say element with ARIA label. Cool. All right. This implementation is actually even a little bit simpler than the last one because we don't have to create that secret label element. So you check out the constructor here. I'm calling super. I'm setting up my shadow DOM. And this time I'm just going to create a button and we're going to pretend like this is like a like a like a fave button or you know, favorite button or something like that. So my button's text content is just this little emoji heart for lack of like a better icon. And I'm going to drop that into my shadow root. I'm still observing this label attribute and observe attributes. And whenever that changes, the trick this time is I'm going to say, all right, cool, take that button, set its ARIA label equal to the new value text content that came in from that attribute. So over in my index file, you can see how that will work. So drop that in there. We'll say element with ARIA label. This time the label, we will say like favorite something like that. So we go back and look at our app. Cool. I've got my little fave button here and let's try these with a screen reader again. Right. There we go. So you've got two approaches now to how you can build accessible web components. You can sneak labeling into the Shadow DOM either with a little label element or by piping through an ARIA label. I also want to include a link down in the show notes to some new standards that we're exploring called the accessibility object model, which should hopefully make this a little bit easier in the future so you don't have to do these workarounds. But today, these are the best practices that I use to make sure if I'm building accessible controls with Shadow DOM that they have proper labeling. That about covers it for today. If you have any questions, as always, you can leave them for me down below in the comments or hit me up on a social network of your choosing. As always, thank you so much for watching and I'll see you next time.