Create a reusable Web Component from scratch
Key Takeaways
Creates a reusable Web Component from scratch
Full Transcript
Hello, my friend and friend, and welcome back to another video where we're going to be exploring web components. Uh, I recently had Michael on to help me walk through what a web component is and using web components that are already created. Uh, but it led to me wondering like how how can we actually create our own web components? And so, Michael, thank you for coming back and joining me once again, but we're going to be, you know, you're going to help me turn this div that [clears throat] I originally created a while ago for these forms with some validation on it into an actual web component uh instead. and we can learn about things like the light DOM, shadowdom, what those are and and everything else that comes into actually creating a web component. >> This is great. You're thanks for having me. You're like a an awesome CSS expert, so I love seeing you branch out into awesome JavaScript things and web components are my favorite. So, let's get into it. I would love to. >> Awesome. Uh, so yeah, I guess as we saw in that previous video, or if people didn't see it, but that's fine. you'll be able to follow along fine, but I had created this and you'd come across it and said, you know, let's make that into a web component. What's the very I guess the the first step? The one thing I will say really fast is I had a in the original video I did, we were doing a lot more in the JavaScript side of things. Uh I've removed all the validation that I was doing manually. We're going to be bringing that into the web component uh itself. So that's gone. All we're doing is checking if we submit. We're now getting like the native form validation coming up in here and nothing else. Uh so yeah, how how would I start by just having a div with a class on it and turning that into a web component? >> Sure. So the first thing is sort of a little bit of ideation of what you want this thing to do, right? So I think the the a great idea for something that has encapsulated logic, encapsulated features and and sort of configuration options uh would be just to sort of handle the validation. Um because one thing to look for is things that you found yourself repeating over and over and over again, right? So you've repeated the the span with the class of error message and the arive polite mechanism and some of those things. Um and anytime you have sort of repeated logic that's that's true for lots of different uh like parts of your HTML. Um especially when they're all sort of the same like they're all in a in a div, they're all in the same with the same structure. That's really right for like a repeatable web component uh to do in the first place. So it looks like that we've got that great situation here and we can just start with uh a custom element instead of a div. So when so here let's just say I turned this into form group instead. >> There you go. >> And I actually just really quickly when I was a teacher back I remember uh way back in the day now. It's been a while since I've been in the classroom but I remember my students love doing this where they would just like make up their own elements and I was like oh you can't do that you know and then I've even had someone in my Discord uh community recently sort of talking about the idea of just using custom elements not even defining them as web components. Um, the one fun thing with this is by default it's inline [laughter] if you have that. Um, but there's nothing nothing else to it. Uh, it's still working probably because of my class that's doing some other things on here. But really like the browser doesn't know what that is. It's just something that it comes across and tries to figure out. >> Exactly. And that's sort of the basis. So a lot of people ask what a web component is, right? And we talked about that a little bit in the last video where the web component is really the umbrella term for a bunch of different sort of browser specs. custom elements is one of those specs that enables sort of the idea of web components to be possible. Um, and so that it's it's great that you can sort of just make a custom tag. You can style that tag in CSS. Normally, you can sort of do everything you want and the the only thing is the browser sort of doesn't know what else to do with it and sort of treats it like uh a div or a span of some sort, right? But we're going to give it some logic in a second. We're gonna say we're going to tell the browser this is not just a custom element. This is a custom element that has behavior. We're going to sort of define it, upgrade it, have some internal logic, probably some shadowdom, right, as well, and and and do the the whole nine the whole million yards. >> So, how do we get started with that? I'm guessing we have we have to jump on over to some Java. >> Absolutely. So, uh the first thing that we need, >> let's see what we can do. [laughter] >> I I'll guide you. It'll be fine. It'll be fine. Um so, the so the creation of a custom element with logic or a shadow root component, it's essentially two things. You have a tag name and a JavaScript class. And then there's a browser API that hooks them together that tells the browser whenever you see this tag name, use this class to run it. Okay. Okay. So, the very first thing that we're going to start with, I think, is to to make a class. Um, and you can name it whatever you want. So, maybe form group, right, is a is a good name. Um, and this is a special class. It has to extend the HTML element. Um, default JavaScript class. extend or >> extends. Yep. Extends >> capital HTML e element. Yep. >> Uh element. >> There we go. Yep. So that's the first thing. >> Really quickly, are we ever extending anything else or is it always HTML element? >> Always HTML element for the time being. And there there have been there have been lots of sort of browser spec discussions about extending other elements because people would would love to be able to sort of extend a button and get button behaviors and things. um that there have been a bunch of reasons why all of those things have so far not gotten very much traction. uh like I I think the last one um we call it customized built-ins and uh it got to Apple and Apple said no for inheritance reasons because if you inherit those behaviors then if they change that class then it breaks everything using it like they they had some pretty valid points but uh yeah you're going to just extend HTML element for the time being and maybe there will be a day when we can extend other elements and get other behaviors of those things built in. U the other thing before we forget, let's come outside of that class and we're going to go ahead and like let's go ahead and hook up the the the the browser API that tells the browser that we're going to associate this brand new class that we've made to that form group tag name that we want even though this class is not going to do anything. Um so if you'll come sort of after the class and on a new line we'll say custom elements and that'll be camelcased. So custom capital E elements.define and that's a function and you're going to pass it two arguments. The first one is going to be the string tag name. So it'll be it'll be just uh in single quotes form-group just like the tag name that we gave over in HTML. >> So yeah, we're defining what >> that tag name. Exactly. Yes. And then uh we'll do a comma and do our second argument and that will be the form group or the reference to the form group tag. So it'll just say form group just as it is in the class on line three. uh form group >> that would be a string. We need to pass it as a reference. So let's take the quotes off and we can sort of refer to that class. There we go. >> Y >> So now basically we've told we've we've told the browser now um that there there's going to be a custom element called whose tag name is form-group and it's going to be run by the form group class that extends HTML element. But we need to make it do some things. Okay. Um so there's a couple of sort of life cycle hooks that we need to that we need to deal with. Um the first one is constructor and so if you've ever written a JavaScript class or any almost any class in any programming language really they all have constructors and the the this function sort of gets run when this class gets instantiated for the first time right one of the good things about web components I think and I think um if you were to ask for [snorts] sort of framework developers like they they're a little bit sort of like iffy on JavaScript classes and how useful they are but uh for me the approach for using classes to do web components is perfect because you have a an instance of a component. Like you're going to have more than one form group on a page, but they're going to be the same thing. They're going to have the same behavior. You want them to sort of share all of this common code and classes that you have the base definition of and then instances of them in your JavaScript like memory. Uh it's like it's the perfect representation of what web comments actually do in your HTML. I love this the the fact that you can sort of create a single class and then you'll have instances of that class for each tag that you put in your HTML that are all separate. They all have different state. They all have different you know um settings and attributes and all those things and then the browser just keeps them away keeps them all off from each other. So the constructor function is uh what are we going to do for every instance that gets created? What's the like when it is created what what kinds of things are we going to do? Okay, so there's a special line that we need to do. So, we're going to declare that this component needs to have some uh shadow route. Okay. >> So, >> yep. So, we'll say this and you're going to use a this keyword, right? And then we're going to call a method that is available for any HTML element which is called attach shadow. So, it be this.attach shadow. [snorts] And then that's a function. Um, so we're going to pass it an object that's going to configure it and we're going to say mode colon open. >> Uh, this is another choice that sort of web component developers have to make. There are technically two different kinds of shadow root nodes. Uh, one is open and one is closed. And the difference is um between a shadow route that you can see and access and query uh like sort of normal webc if you ever come across them um uh in the wild and then mode closed which would be like your audio and video elements from from HTML where they actually are shadow root elements but the shadow route is closed and they're not accessible. You can't look at them. You can't you can't open them up and see you know what the internal DOM is. You can't query into it. you know, none of the query selector is going to work or anything in this case. I think we'll probably just do open. We don't need to like completely hide all the shadow root internals from from from folks. Um, so this so this this line will say to the browser, we give this component a shadow route that we can use and give it a shadow root property and give it an inner html and all of those fun things. >> Yeah. Just really quickly, is there a like is there a specific case where you might want to use closed because I can't like for this type of stuff that a regular person would be working on? >> Could I guess conceivably it would depend on how much you want to lock your component down and I and I genuinely think that these days we we're probably going towards not locking components down as much and sort of leaving them more open. Um, it there could be there [clears throat] could be use cases, I guess, where the internals are so complicated or so verbose that that you would just sort of hide it all. Um, but it's I think it's going to depend a lot on who your sort of consumer base is and what they're going to be using for it. it if it's going to be something where they need to have like um testing capability or something to use some internal parts like there's a button inside that they need to click with testing or something so they have to query it then just mode close just won't work. Um but if it's a component that could be completely blackboxed and that you don't have to like interact with any of those elements inside the ter template then maybe then maybe close could work. But I've only ever written mode open. I've never written a closed one. So it's more of a niche and if if you're in you know default to using open and if you have a very specific use case maybe then exactly and that's part of like the way that the the the shadow route sort of spec was uh originally produced is that the [snorts] shadow route and shadow DOM was available as an internal browser feature for audio and video for a long time and sort of the the the addition to creation of custom elements and web components was sort of exposing that private browser API to as a public API and So part of the reason we have open and closed is that the browser kind of had open and closed before because it there's actually a browser setting that you can turn on where like inputs are actually shadow roots. Like you can turn on C see C browser shadow route. So you can see that an actual a native input uh when you turn it on through dev tools um actually is just sort of divs inside. It's like it's sort of just divs that have other other things in it. Um, which is which is very curious, but you couldn't tell without that special browser setting to turn on, you know, view um view browser shadow. I I forget what the setting is. I hide it on for everybody, but >> I [snorts] have it in my other I'm running Canary right now and I didn't have it set up here, but normally I can see it. Yeah. >> Yeah. So, um, so that so this line is going to say we're just going to sort of stick with mode open. Um, in a little bit we'll sort of do styles, but let's go to let's go ahead and set up our internal shadow root template, which is sort of let's let's what is the HTML that is going to be the guts of our of our component. Okay. Um, and so we'll just do that now. Now we have a sort of shadowroot property. So we can say this shadowrootin HTML. Yep. And then we can set that equal to some HTML. Now you can you can get a lot more complicated than this. You can like there's there's there's lots of different ways that we can sort of make this template be reactive and change when when things attributes and properties change. For this use case, we don't need to change this template at all. So, we can just sort of set the template and then manipulate it with CSS and and JavaScript. So, for this case, we'll just do a string and we're going to say uh we're this string is going to contain some HTML in it and it'll just say, you know, uh angle bracket slot. We'll sort of put a slot element and we'll open and close it. And then after that we'll put the uh a span for our error message. >> Okay. >> Error. >> Yep. So we'll do error message for sure. And uh the original one over in the index.html that's what we're going to be replacing. Uh is using an ara live polite technique where uh to announce it to assist of technology. um the the span has to always exist but it'll update when its content changes. So that's another reason why we don't really have to update like uh dynamically update whether or not the span is or is not rendered. We can just sort of always render the span and then change its contents and then the the changing of the contents will announce with ROI polite which gets it perfect. So we can just copy that and now we have an internal template right and so if we could see where this is used right now we should be able to see if we've used it in the HTML we should at least be able to see that span appear >> for any place that we've >> because here where I have the oh we have let me >> yeah we have to delete that one y >> do we have this do we have our do we have our script linked or we we're working on form group JS we have it linked okay >> and then We we're calling our define >> game is that's okay. Uh >> we're not getting shadow yet. We may need we may need super. You may need to add super in the constructor. Yeah, >> be right here. I think super. >> Yes, that might >> There we go. Okay. Yeah, that was a step I missed. Yeah, step I missed. Yeah. So, calling super uh tells the constructor to run the constructor of HTML element first. because we're sort of we want to construct the the parent class so that we can become an HTML element. So without that you're you're you're not really you're not you're not doing anything and attaching anything and your your inner HTML stuff's not working. So uh we'll call super and then uh we'll have our shadow route with our slot and our span and everything is fine. Yeah, we nothing has changed visually because this when the slot um is available in the template then all of the child um children elements of that foreign group um tag are put into that slot. So we call we call this slot here the default slot which means it does not have a name on it a name attribute right um and we call it the default slot because when you put one when you put a slot with no name then the browser says all children of that tag that you've defined will get put into that slot tag and sort of projected in there. So by doing that we we we've said the label and the input go as children of that slot and then the span comes after both the input and the label. >> Right. >> Yep. Perfect. >> Okay. >> So that's that's pretty much our constructor. We'll do some styles in a minute. Um but first we sort of we we want to like do we want to do styles first or do we want to sort of set up uh event listeners and things? Um, I guess let's do an event listener just so we can see the error message do something maybe. Um, or even just for styling or whatever it is. But yeah, let's do I guess a simple one maybe just to >> sure >> go down that route. Yeah. >> So for event listeners, we're going to do another sort of life cycle hook, right? Because the constructor for a web component runs before the element is actually sort of on the page. Okay, but there's another life cycle hook um that sort of guarantees us that the component is actually on the page. It's actually part of the DOM and being drawn by the browser, right? That life cycle hook is called connected callback and it's a function that runs every time this element is mounted to the DOM. Um theoretically, this could be called more than once. So, we want to be careful about what we put in here because if you take an element and unmount it and then remount it, like if you're um if you're using JavaScript to sort of move an element and you sort of you unrender it and rerender it somewhere else, but you save a reference to it in JavaScript, then technically this connected callback can be called more than once. So, we want to be a little bit careful, but I think for this simple use case, we can we can set up our event listeners there. And that's where we're guaranteed to have the children, you know, existing and we're guarant we're guaranteed to be have like we're in a DOM. We have DOM APIs we can use. Well, the constructor we were not necessarily guaranteed yet. >> Okay. >> So, we'll do inside the class still sort of after the constructor. So, yeah. New line after 13. Yep. We'll do connected callback as camel case. >> Yep. Oops. Yep. That's perfect. and then we will start establishing our event listeners. Okay. So the first thing that we're going to do is sort of we need to know our target to set all of our event listeners on. Okay. Um and in this form we have a mixture of sort of inputs and text areas but pretty much everything is either an input or a text area. So we need to find probably just the first one or the only one. Right? We're sort of we're guarant was we're not guaranteeing that nobody will ever put an in two inputs inside of this web component, but we're saying that if you put a second one, the input's not that the component is not really going to use it. It's going to use the first one that it finds, right? So, I think we could just say uh we could declare an but we probably want to hold on to a reference to that thing. So, I at this point I would set up a private property on the class so that we can hold on to the reference of what the input is that we found. Okay. Okay, >> the way to do that is let's come up to the very top of the class and we're going to declare a new class property on line four and let's call it input, but we're going to spell it with pound the pound sign and then input. Okay, and then a semicolon. Now, what that's going to do is it's going to establish a new class property and you'll you'll sort of refer to it by going like this.pound input, right? Okay, >> but it's private. This is JavaScript private variable. So, so um from the outside of this class, you would not be able to sort of use that input property, right? Because it would be private to only this class, >> right? >> And then in the connected callback function, we'll use it and we'll sort of set it. So, we'll say this dot um pound input equals and then we'll do a query selector. And this this one is fun because this sort of can show you the difference of querying from inside a shadow route, right? or from inside a web component because you can query both lightdom and shadowdom and it's a little bit different. Okay, but this time we want to query lightdom children. So we can say this.query selector and then we're going to pass it the selector we want which is probably just input text area something like that right just inputs and text areas and you'll pass it as a string. So query selector takes a string. So all of that stuff is going to be wrapped in single quotes. Yeah. >> Yeah. Wrap the whole thing. So they And so we're going to pass it one one argument. So it'll be >> quote input, comma, space text area quote. Yeah. Exactly. Yep. CSS link. >> Yeah. >> And then so now >> for every instance of this class when it's connected to the DOM that input property that that pound input private property is going to be set with whatever the reference is. Now we're expecting that there is one there. So, we're not going to handle the null case of what if there isn't one, right? We're going to go simple. Um, and then we can use this input property to start adding event listeners to our component. So, maybe maybe we should check to see if there is one there. So, I do an if, you know, if this input this this pound input um because we only want to add the event listeners if the input actually exists, right? Then we'll say this.input input addeventlister and we need to give the event name. So we're going to going to pass uh two we're going to pass two um two pars. The first one is the strings the event name. So let's do invalid for this one. We need invalid event listener and let's let's just put a function reference here that we'll fill in later. So we'll just say you know pound handle invalid or something. uh handle uh handle invalid for now. >> Now, that's just going to be Yep. that it's not defined yet, but it's going to be actually it'll be uh this handle invalid, >> right? Yeah. >> Yeah. Sorry about that. >> Yeah. >> Okay. And then we'll we'll fix that in a second. But let's let's sort of copy this line and do the same thing, but we're going to do it with two other events. We're going to do input and blur. And um and I can tell you what these are going to be about. The reason we need an invalid Yeah. input and blur. >> Yes. >> Uh and we don't need the if for every check. So let's just do the single if and then >> um >> Yeah. >> No. Okay. That easy. Copy paste. So just >> Yep. >> That's the one. And then >> is that Yeah. >> Uh and then this one was the on input and this one was the blur. >> Blur. Exactly. And then the maybe our our handler functions are handle handle input and handle blur. >> Perfect. uh handle input input and handle blur. Do we want to leave some comments for ourselves to know what we're what we're doing? >> Sure. >> So the handle invalid is going to be turn off the browser popup. We're going to that that we're going to use the invalid event to turn off the native browser validity popup. The input event, we're going to use that to hide error messages while the when the user starts typing. And the blur event is going to be to validate and show error messages after the user's done typing. User AP. >> There you go. Cool. So, that's essentially the setup. There's a little bit more to it, but we'll get back to that in just a second when we sort of define these hand motions. Um, but the important thing here is to talk about, and this is something that authors of web components deal with a lot. Okay. Um, event listeners are tricky because they have a reference to the element itself um that the event is attached to and it if you don't clean up your event listeners and like remove them, then you can lead to memory leaks. You could have you could have listeners that are that are like don't have any attached elements that are just in memory and and all these kind of weird bad things, right? Um it says if you're adding an event listener to an internal element that is internal to the shadow roots template, like if we added an event listener to our span that we wrote in the template, then that event listener is automatically going to get cleaned up by the browser if our form component ever goes away, right? So we don't have to worry about sort of internal ones. But this one's a little bit different because we're actually adding an event listener to an external element in the light DOM that isn't necessarily going to get gone away just because our form group component gone away, right? Went away. So we have to sort of spend some energy and clean it up ourselves. And so we're going to use another life cycle hook for this which is the disconnected callback >> which is this will get called whenever the form group web component gets unmounted from the DOM. So this is one of the reasons why we set function references to um to handle our events because we need to do it we need to sort of use this for cleanup because the only way you can remove a listener is like removing it by the reference to the function that you called. So we have to sort of save it as a reference. So what we'll do here is um we'll say this dot uh input rem remove actually let's do the if as well just in case if we don't have an input then we don't have to remove right. So the same sort of if if the input exists then we'll do this.pound input remove event listener >> remove listener. >> Yeah. And it's pretty much this the exact same structure as the ads um >> uh above. Yep. >> Uh invalid and uh I'll just >> this handle. Yep. Could probably copy all those lines and then just change ads to remove. Yeah, >> we'll do that. Uh that one and I'll clean up the indentation because I probably don't have prettier running. Nope. Uh and then so that would be a Whoops. Copy that. And both of those can be removed. There you go. So this will ensure that if our form group form group component is ever unmounted from the DOM for any reason that those event listeners are also removed. That way we're not if let's say there was a situ situation where the component was unmounted and remounted unmounted remounted unmounted remounted. If we didn't if we didn't clean up we might have six listeners that are all on the same element and you'd have all different kinds of things firing twice and seven times and so it's pretty bad. So now it'll be cleaned up and we'll be pretty sure um uh that we're that we only have one event on every time it's every time it's mounted. Um should we implement these handlers? Should we do >> Sure. >> Yeah. >> Figure them out. Okay. So the let's do the invalid one uh first. I think that one is the the simplest one. Um, and we're going to, so we're going to make a function with this has that same name. So, pound handle invalid some somewhere above the callbacks here. It doesn't really matter where. >> Yeah. >> Handle invalid. >> Yep. And that's going to be a function. >> Function. >> And it's um it's going to get called by the browser when the event gets triggered. So, we can um we're going to need that event as a parameter. So, you can just say e. Yep. And then the very first thing we're going to do that that turns it off is e.prevent default. So that says prevent the default behavior of whatever the browser was going to do because of this event. So that that just turns off the pop-up. That's the easiest thing. Um and then we probably also want to sort of work on displaying error messages um when this happens. We we can we can do it on blur but I think we can also do it on inval invalid as well. Um so we can get to that in a minute. We'll just do the logic in blur and then copy it into this invalid handler. Um the next one to implement would be the input. So let's handle the input. Now remember we're going to use this input event to hide error messages while the user is typed in. >> Yep. >> Correct. Um, so for this one, we actually don't need a reference to the event at all. So you can just sort of leave the E out. We don't need that one at all. But but we still want to check to see if there's an input. And if there's an input, then we need to sort of erase the error message from that um that element. Right? So another thing we can do is we now we sort of need a reference to the um the error message span, right? Um so we're going to sort of back up a little bit here and get ourselves a reference to that span that sort of that is calculated whenever we want. So there's a couple ways you can do this, but um if we do if we go up beside input and add another property. >> Sorry, we're >> on line four. If you go up beside input, yep, >> and make another private property, you can say pound error message. And then you can set it equal to um this shadowroot.query selector uh and then pass it the selector that we want. So it can be you know dot error message or span or you know whatever it is in this case. >> Now this one this query selector is actually querying in the shadow route now. So we're not querying lightdom children anymore. We're we're querying our own shadow route in HTML template that's from line 11 to 14. Yeah. What's there now? Yeah. >> Um so back inside our handle input, we can say um if exists, right? If we have that thing, then we're going to um set the text content of the error message to be nothing. And now this is part of how we're handling error message styling and displaying and assist of technology sort of um uh announcing is that the error message is shown when it has content in it when it has text in it and it is hidden when it doesn't. And we'll get to the styles that do that in a minute, but that's the approach that we're going to take because we need that span element to stay there all the time. And so the approach that we're going to do to get it to read out is to fill it with content when there's an error message and then remove the content when there's not, right? When there's no no error. So we'll say, you know, this pound error message. So we're going to use the text content property. We could use inner HTML if we wanted to, but we're sort of only doing text. So text content, it makes sense. Um equals uh empty string. So we'll just this is just saying erase the content and when it is empty it'll be hidden. So, we'll sort of hide hide the message. >> Good so far. >> Yep. >> We'll check in. Is everything making sense? Everything sort of feeling right? >> I'm I'm following along so far. Uh yeah, perfect. >> Perfect. Okay. [snorts] So, then the last step, I think, would be to implement the actual sort of um error checking. And this is where it gets a little bit more complicated. So, we're going to implement the blur handler because when the user tabs off the field, we're going to sort of uh we're going to check the browser's default validation and then use that to display our error message. Okay, so we're going to have handle blur. That's exactly right. And then we're going to do a little bit more complicated if check. So if the input exists, so if this.pound input and so the double amperand, yep, double amperand. There we go. >> Yep. So we're gonna say if it exists like if if it's not null and it's invalid. So there's a so we can sort of use um the default validity state of that input that the browser is automatically going to calculate for us and do this check. So it'll be [snorts] if this pound input dovalidity dot valid talid but now we're but we need to reverse that because we don't we say we say if it exists and it isn't valid then we're going to do something. So we're going to put it yeah so we're going to reverse that with exclamation point. Right. So in this case of we have an input and it isn't valid then we need to display the error message right. So we're going to do the same line from uh above in the handle input but we're not going to set it to empty string. We're going to set it to some sentence that is the actual error message. Um for the time being we can just say this error message.extc content equals um you know you're done goofed or something. We'll send it to a string and we'll come back and customize it in a second. There we go. Yep. So now that should work for uh when you blur off, right? Unless we've done something wrong. Oh yes. Okay. There's one other thing that we need to do uh in the connected callback. >> Yes. >> The the function >> uh that we've passed. So the this handle invalid. This handle input handlebar. >> By default, and this is a little bit tricky. This trip this trips up people a lot. By default that function is called with the scope of that function. It's not called with the scope of our class. So the this keyword doesn't doesn't like does it corresponds to the target element that the event was emitted from not our class. Okay. So on all of those functions at the very end we need to add a bind with paren this. >> Yeah. This Yeah. If you're doing events this way, it's a it can it can trip people up a little bit. But basically what we need what we what we're saying here is take this handler function and call it bound to our class instead of bound to the target element. Right? And then inside of our handler now the this keyword will be will be referring to the class that we're writing and not the target element that the event was emitted from. Does that make sense? >> It makes sense. It's weird looking at it. I understand it trips people up a little bit sometimes. Yeah. So now we're saying, you know, like call call that function but then bind it to u bind it to our class. So that's definitely one thing to do. Um we may need to log out and sort of see what's make to make sure that it's being called. Um this would be a good thing to do. >> Okay. Do you want to just sort of uh put a console log in our handle input and just sort of make sure that we're >> Yeah. Uh handle input log. Yep. And then that should query selector. Oh, I think Oh, I know why. Okay. Um let's fix this. The er what's erroring is the error message property. I I just I um gave bad advice. So, [laughter] so remember when we talked about um the the only way you're sort of guaranteed to um have DOM and have the shadow root property is in the connected callback or after the connected callback function has run. Well, our error message property on line five is doing a query selector. But the DOM doesn't exist yet. We're not in the DOM yet when that when that property um is applied. So a good way to do this um I like to make a sort of a getter function that will just be evaluated whenever whenever it is whenever we sort of want to to uh pull the value of that property. So, u we can change this and we'll come down come down to after your constructor and we're going to say um a very similar a very yeah you can do it after the constructor doesn't like formatting doesn't matter if you were >> you know if we were in design system land we want all our components to be consistent but we're not so that's okay um we just need a property so state we're going to be inside the class I think right now you're outside of the class but we still Yep. Um, so we'll say get error message uh as two words. So we'll use the get keyword space and then lowercase e error message. >> Get space. >> Yep. >> Message. Yep. >> Message. >> Yep. And that's a function. That's going to be a function. So we're going to have PNS and curly braces. >> And then we're going to return the same query selector that we had in that pound error message private thing that which we can now delete. So, we'll copy that shadow root query selector and paste it here, but we're going to return instead of setting that prompt. Yep. >> Keep deleting until um you get past the equal sign. >> Sorry, >> there you go. >> There we go. So now what this does um this is essentially defining a property. >> Mhm. >> But um it is defining a property as a function as a getter function essentially. So you so once this is defined you can use this like this error message the exact same way you would have used the other property except that you're sort of giving it more behavior. Does that make sense? >> Yep. Yep. >> It's you're not merely setting a value, you're doing some computation, right? Um and so to keep our code working, let's add a pound in front of the error message on line 58. That would that will make it a it'll make it a private property and that should get rid of our errors. >> Errors. Good sign. >> There we go. There we go. All right, now we're working. So now we've got our event listener, right? And and and that's and that's working. I think we've only put the form group on the first element so far. So that's the >> just form group. And now we get it showing up on both of them. Perfect. Awesome. >> There we go. Um and we don't have styles yet. So we haven't like So basically the span element always exists for the time being. That's why you saw the extra space happen. We'll do the styles in a bit. Um well we've got the basic logic of handling the um input handling the blur and turning off the the browser uh default. Now this is invalid. If I start typing it immediately goes away because we set that up >> uh to be empty on the handle input right here. We're we're removing the error message while I'm typing. Perfect. >> But there's but there's there's still a problem. um no matter what the error message is, as long as it is invalid, the error message will always say invalid. And we probably want to give people more information about what the actual problem is, right? Um and so we want to give them a little bit of a customized message sort of depending on the kind of invalid that the value is, right? Right. Um, and there's a couple of different cases. We talked about in the last video that it's, you know, value missing is sort of the constraint, the the default constraint validation case of the input is set to be required, but you don't have a value in you, right? >> Um, there's there's a bunch of other ones. There's too long, too short. We talked about, you know, pattern mismatch, range underflow. So, we probably want a custom sentence, a custom phrase for each one of those situations. And then it's probably a good idea to have the user be able to customize that somehow because our defaults won't work for every use case. You know, sometimes you want to have um an error message that is um that says something, you know, please enter a first name for this field, but but the component, this form component doesn't know that it's a first name input that we're surrounding. We don't know that kind of thing. So, but the next step would be to make sure that our text content of our error message on line 30 is not always just a hard-coded string, but is sort of a configurable um error message that it has configuration from the outside, but then also default. So, you don't have to send anything if you don't want. >> Yep. >> So, shall we set that up? >> Yes, let's do that. >> Okay. So, the first thing that we're going to need to do is I think we're going to need to like figure out what the error message should be. Okay. So I think a good thing to do is get do another one of those getter functions, but in this case it's going to be for the error message. So like maybe let's make another get um and then let's call this property custom error message. That's also going to be a function also with curly braces. Yep. Now, the thing that we probably want to return here is, at least the thing that makes sense to me is like a lookup object that's directly aligned to those like the cases that can be wrong and then the sentence or the phrase that we want to display in that case. Okay. Right. >> So, um I've taken the the liberty of sort of like writing all these out um because there's a lot of them and it's sort of pretty repetitive. So, if we want to just paste in the the object here and we can sort of talk about >> um >> there we go. how that works. There we go. So all these properties in red are the direct validity properties that exist on any sort of HTML form element by default. So any input type text or type number or any text area, any radio button, um you could query selector it and then get its validity value missing property or it's validity.t long, validity.t2 short. um it goes right alongside that validity valid which is the the overall if any of those things are are invalid and the the validity valvalid is false. Um and so for each of those cases I think it it it makes sense to me to sort of align my lookup based names of those specific states that I want to handle. >> Um and then so those are the keys to the object that we're going to use to look it up. Um, and what's going to be cool is we'll be able to pass that validity like the object in that validity directly to this lookup as the key to look up the the message that we want to grab for. So, we sort of pass it straight to it. And then the the right side of this this the value um is our customization and our default. So we have a big or that is either the value of an attribute that we're going to support or a default sentence whichever one is defined. So if the attribute is defined and has a value then we'll use that and if we don't then we'll use the default. >> Perfect. It's a pretty clean way to sort of offer a customization but then also have a default and just keep them all in the same place. And this since it's a custom getter function it's going to get evaluated every time that property is requested. So every time we write this custom error message or pound custom error message, it's going to evaluate this lookup, it's going to call the this.get attribute every time. So it this is automatically sort of accepts changes to the attribute. Um so if the user like the the consumer were to change the value of the attribute from dynamically, then it would be updated in real time and the next time it would be the the component were um the input was invalid, then it would change that attribute value and and show you something different. >> Yep. So we need to um we have so we have our lookup object and now we just sort of need to um use it right and so the way we're going to use it is first we need to like get a sense of what is the error message that we want to display because remember we talked about an input can have multiple things wrong with it but we really only want to only want to display one error message at a time because that's a better user experience and we don't want to list six things that they're all wrong. So given that all of these things could be true at once, like all of these invalid states could be true at once, we need to have some mechanism that kind of decides between them um which one is the one that we want to choose to to display. So for that one, I just wrote another one. You can just write another little uh another little getter function. Uh and I I I think I call this the get first invalid. >> Um first invalid. Yep. So we'll do get space pound first invalid. >> Yeah. Or first invalid is what? Yeah. And actually let's just make it let's not not make it a getter. Let's make it a regular function. So let's take the get way and we'll just have it be a function that we call. There we go. And this function is going to be um responsible for deciding which thing that we want to display. Right? So, we need to loop through all of the keys from the the the native validity state of the input. Not those, the ones that actually come from the validity object in the input, right? We want to loop through them and find the first one that's invalid essentially. >> Okay. >> So, we'll do a for loop. >> So, we'll say for uh const key in validity state, and this is actually going to be a variable. So let's pass that in on line 62 to the get first invalid function. We're going to actually pass that validity state along. >> Um >> so so yeah, add it as a parameter to the get first invalid function on 64. Yeah, >> sorry. I'm >> we're good. So in the parentheses on 62. Yeah. Right. So we're just add validity state there. So we're going to pass it into that function and then use it. So we're going to loop through all those keys. Right. And then we're going to say if uh validity state square brackets key right now I I'll explain this in a second. [snorts] Um then return key. Okay. So what happens is if you were to look at the validity object, we can actually you can actually pull it up if you want to. Um if you want to uh console or open up the console >> y >> um and inspect one of the inputs, you can actually um grab it and then go to your console with it highlighted and type dollar0, which is a shortcut for this stock query selector, this element, right? Uh validity. Yep. Now expand that open and you can kind of see um all those objects, right? So it it's it's it's basically a big object. Can can you not like um >> seem to >> Oh, you can't give it an object structure. Oh no. >> Um it's super helpful like kind of see it as an actual object. Maybe hit uh hit enter and it'll echo it maybe. >> Right. Yeah. >> There we go. There we go. >> I was like this works. It just has [laughter] Where's the arrow? There we go. [gasps] >> Okay. So in this case like valid is false because there is a value missing that is true. So it the validity object is a little bit backwards in terms of um the way that it's structured. The way that it's structured is >> like if you're reading this object it like it is true that the value is missing. >> Right. >> Right. So the truthy state is whenever whenever those cases are truthy they are invalid. Okay. So that's essentially what our um our loop is doing is to say if the validity of the validity state of the key is truthy. >> Yes. >> Then return that key because truthy means it's invalid for that reason. >> Yep. >> Okay. [snorts] >> Yep. >> So that's our get first invalid function. Um and we're just going to and that's and that's going to find the key and that key is the key we're going to use to look up from the custom error message. what s what what sentence what phrase to use. >> Yeah. Because whatever whichever of these then it matches what >> it's a little bit convoluted. Yeah. But we but basically we're sort of passing the keys around from the validity object to get this the sentences that we need >> um and then customizing them with attributes. Right. And so if we go back to our blur where we're handling the um the the use case instead of setting the text content to that string of invalid now we can set it to uh something more clever. So we can say this custom error message >> and then we're going to >> custom error message. There we go. Yep. [snorts] And we're going to pass it an array or uh it is going to be an array, right? Yep. And we're and the the key that we need is the the one that comes back from first invalid. So if you want to just copy the line um from from the the code there, I took it I took the uh liberty of sort of writing that line out um in the code and then we can sort of explain what it's doing. There we go. So, so the custom error message is returning an object that we can use square bracket notation to sort of get to the key of the get first invalid is taking in the inputs validity and returning the key that we're going to use. Does that make sense? >> Yes. So, yeah. Yeah, we're getting this custom error message. Get the first invalid. So then it goes through our well gets that finds the first one matching it to what we have here to get the message that we've written. >> Exactly. Y >> So it's the text >> if that were like not as readable like you could definitely expand this out and sort of like do you know um not do it all as a oneliner um to to make it sort of more readable to know what's going on. But essentially this is looking up in the custom error message lookup object via the first invalid key from the validity of the input. >> Yep. Yep. >> So now if we were to save and blur, right, then when it is invalid, now we're getting the default. Now we're instead of getting the invalid string, now we're getting the the our default case. And then we can sort of jump over to HTML and double check that the attributes working um as well. So we can come over here and say value-missing- message equals something that looks different than the default and we'll sort of know that it worked. >> Hey, there we awesome. So now we have that validity. We have a custom error message. We have a default error message. We've got, you know, user experience things of not showing error messages while people are typing. And I think the last thing to do is style it. Do we have I think we've got pretty much everything to do except for um working on the the getting this thing styled. >> Awesome. And that's I was going to say we haven't written any CSS in a little bit here. I'm getting antsy. [laughter] >> We'll take care of you. We got I got you. So to style um in a web component, especially we're we're all very vanilla, very native, right? If you were using a library like lit um it would handle the stuff sort of um sort of thing for you sort of under the hood and and make it very easy. But we're going to sort of do this the native way to short to show how you would do this uh sort of vanilla web component from scratch. Okay. So what we want to do is we want to create a stylesheet essentially in JavaScript and then adopt that stylesheet into the shadow route of the component so that the styles in that stylesheet become shadow root styles and become encapsulated and things. Okay. So what we'll do is we can you can actually instantiate a new stylesheet in JavaScript. So, we'll just say const, you know, form group styles equals new CSS stylesheet. >> Yes. Uh, new >> Yep. CSS >> capital S style. >> Three S's in a row is style. >> Yes. >> Yep. >> Uh, >> as and that's a function. So, you'll say function and then and then uh uh you don't have to pass in any parameters or any uh um curly braces. You can just put a semicolon and be done. Yep. So we have our form group styles is our new style sheet >> stylesheet. Perfect. >> Exactly. Now we have to fill it with some rules and and this is kind of a really funky API. I'm sure it's named name named for a good reason, but uh we can say form group styles dot replace sync. And replace sync essentially says it's a function and we're going to pass it a big string. the string is going to be the CSS to like replace all of the rules with for this stylesheet, right? So bas this this is basically an API that's like >> replace all of the rules of this stylesheet with this string of CSS that I'm going to give you. Now there aren't any rules in here yet. So we could sort of add instead of replace, but replace is easier. >> Okay. >> Um and then now now we get to write some CSS. um we get to write some regular CSS and we get to write some sort of special uh shadowdom uh web component CSS. >> So the first thing that we want to do is set up our er error message span sort of being hidden when it's empty and being shown when it's not right. Um, and I know you've done videos about this before. You the empty uh pseudo selector >> first message [snorts] empty display none. >> Yes, exactly. Yep. So, when it doesn't have any content in it, because remember our approach is the span always exists. We're going to fill it with text when there's an error message and we're going to delete the text but keep the element when there is no error message. So, we're always going to have that span. If >> I find that, it should Oh, no. We I was going to say we should see it, but we haven't attached that to anything yet. >> Not yet. Nothing yet. >> We're creating the styles, but then we have to actually >> we have to hook it into the component first. Yeah, exactly. >> And then the second one is we need to make this a color. We probably want our error message to be red, right? So, we're going to I think the best strategy for this is to use a um a CSS variable for this. Yeah. So, let's just sort of, you know, assign the color of error message to be uh an a CSS variable that we want to or a custom property that we want to just name. I I think error message color is a good one, right? Um but it could be sort of anything you want here. Now, this one is interesting because what we're saying here is we are not necessarily going to like we we could define this custom property >> in this CSS but if we do that and if we do it here in this uh error message with class selector in the shadow route then it actually won't be able to be reset from the outside of the shadow route boundary by a consumer. Right? Before we before we go on that, I just if I did root here, that would work. >> Root would not work here. >> Root because I was like there isn't really a root if because we can't access the HTML element. That's okay. >> Nope. >> Perfect. So, um what what we can do here is and this is this is a technique that I like to use for sort of what I call public CSS custom properties that are usable. They're they're designed to be set from the outside, right? So there so from the the perspective of the web component u it's that these CSS properties are part of like the API of the component. I want consumers to use them to do configuration things on purpose. Right? >> Um the way to do this is to use the variable as if it exists but set your default color as the fallback in case it doesn't but never to set it >> to a to a value. >> Yes. because you want it to be overridden. Does that make sense? >> Right. Yeah. So, yeah, because if you were to if you define like if I define error message color then it's set because we can't redefine it or the user from the outside can't redefine it. Whereas if normally custom properties are inherited so it will if somebody sets it in a regular stylesheet it would come in and actually apply. >> Exactly. Now there's one exception. So we can do this if you want. Um there's one exception to that rule which is if you define the variable or the property in colon host in the colon host special sort of shadow route selector then it can be overwritten because colon host specificity is very low. So you could say colon host uh d- error message color colon you know and then whatever the fallback you want it to be right then it can be overwritten but only if you set it in the host selector if you set it any sort of like further down in in another selector that isn't host then it'll be in part of that encapsulated CSS and won't be able to be uh reset or overridden. Okay, host is kind of special because host means the tag name itself. So the co the colon host that we're selecting is the actual form-g groupoup element itself, but we're selecting it from within the shadow root boundary styles. We're like reaching outside a little bit. And so that's one of the reasons it has low specificity is because >> we want the outside world to be able to win on that tag, not the c the styles itself. Because the way that I think about it is I think of the the host element tag itself as the outside and the shadowy elements the DOM element as the inside and as the custom element author I own the inside but I don't own the outside and so I want the application developer the consumer of my component to own the tag itself that I'm defining right. >> Yes. Yep. That makes sense. >> Making sense. So so this is a way you could do it as well. um it's a little bit longer than just sort of putting comma red, you know, uh on the color prop where you're going to use it, but but it's the same approach. It's essentially the exact same thing. You could define a variable in host. It can be overridden and then it's used with no fallback on your property that you want to use it on or you can not define it and then set your fallback color as a fallback where you want to use it. >> Yeah. >> Either way. >> Either way gives you the same result at the end. >> Exactly. Yep. the ones one's shorter, one's longer. You know, sort of your mileage may vary about which one you'd want to do, but they're functionally the same. >> And then the last thing we want to do is attach this stylesheet to the component to sort of make it >> uh stylesheet um shadow root styles. So, we're going to use the adopted stylesheets property that's on our shadow route. So, we're going to come inside the constructor because we're not doing DOM stuff. So, we can do uh do this inside the constructor. And after we attach shadow, we can say this.shadow routt dot adopted stylesheet equals and this needs to take an array. So we're going to pass uh square brackets and then our form group styles uh variable that we created above >> uh form group. >> Yes. And now we should have a red error message >> that is also being hidden and shown when not filled. So we should see the the yeah the query type. Uh yep >> it's overridable. It is hiding and showing. And there's our styles. >> And so if I went just here quickly. This was some things we were playing with last time that I didn't get rid of, but we can talk about that in a second. Uh, but here we'll say that if I just did um, >> you got air message color on lab 17 already. >> One second then. I was going to ask about that. Uh, so if I have that and then that's blue. So here error error message sty what did we call it color color [snorts] is yellow which we won't be able to read that's not coming through and then if I did it as >> oh there is there is one thing to talk about so >> y >> there there is actually a subtle difference between the two approaches let's um I forgot about this um no problem so if we go back to our javascript Yes, >> if you define the variable like this where you're defining on host and then using it deeper, you can >> Oh, >> you can set it, but you can only set it on that element. So, you have to select the form group element first. You can't just do it on root u because it's being set on that element. So, you have to reset it also on that element. >> That makes sense because that's >> I tend to do the fallback. Yeah, that that's the exact same thing as like I'm defining something here and then on a regular whatever form my form group and I say my error message color is green whatever like this will take precedence over what we have there. So it's the same thing that's happening >> exactly >> where >> so I tend to do the other way where we um just sort of set the the desired sort of default color to be the fallback. That way you can the root one will take precedence and it's sort of easier because you don't have to select that element specifically. You don't have to do tag name selectors and things like that. Yeah. >> Go. Perfect. Now there's one other feature that we could add if we wanted to sort of give ultimate support to our consumers, right? Um because again we're in Shadow Routt land and so we're very encapsulated and so we only sort of give configuration options that are allowed. Um, technically it's JavaScript and HTML, so you can do anything you want to. It's just sort of clunky to to reach in and and add things if you want. But, um, if we want our user, our consumer to sort of be able to style something that's more than just the color, we got to do one other we have to have give one other thing because our our we could we could probably add some more CSS properties that style other things. Um, but we currently don't have any things like padding or background color sort of already established with default. So, a good thing that we could do is add a CSS part to our error message. >> Okay, >> that will allow outside CSS to select that part and then style all of the CSS properties on that element if there need if their need arises for that, >> right? >> Does that sound like something we should add? >> Yeah, that sounds useful. >> Okay, so if we come to the span class error message, we're going to add a part attribute and give it a name. >> Easy. >> Part equals and then error message or something. Yeah, something like that. >> That's all we really have to do. The part is a special attribute that will uh that translates into CSS and it purposely um like it it pierces the shadow root boundary using the colon colon part um selector function. Um and so if if you want to do that in your CSS now now you can select sort of form-group colon part PNS and then give the name error message, error dash message, right? Um, now you can style lots of things. You can style padding, you can style background color, you can style border radius, you can sort of do whatever you need. >> Yep. >> There are a couple things to know about this, but both from the consumer perspective and from the author perspective. One is that the part name is not a selector. And I actually love that it's not a selector as a component author because it it being a sort of just a string a name that's not necessarily connected to the DOM element that it lives on >> means that as the component author I could move that part around to different DOM elements and not break anything as long as the part name is still sort of has has the same reason to exist you know for for the component when I make changes if If it were a selector and maybe I changed what classes were in my template or something, I changed what ids or what, you know, whatever, I might be breaking that selector. The fact that it's just a string, sort of a name that's not connected to the DOM element that it lives on, uh, means that I can maybe move it around a little bit and then not break it and break anybody's sort of CSS that they're styling from the outside as at least as much or not as much, which is great. Uh, and the the other thing too is when you're styling a part, you only get access to the DOM element that the part is on. You don't get access to any other combinator. So, you can't do children, you can't do um sibling selectors, you sort of can't select around it. Again, this is sad encapsulation at work. But when you style the part, you get just that DOM element. You don't get anything else. [laughter] >> Yeah. Yeah. That's a a good thing to know because Yeah. Uh so if you did want to expose like say we had I don't know why but you had another span in here this would you know internal message or whatever and then you could have that as its own thing that people could select if they needed to. >> Exactly. So they basically what ends up happening is that you know webin authors have a collection of CSS properties that they use for customization and a collection of parts that they use for customization. a selection of slots that they use for custom, you know, customization which maybe have names. Maybe you have a uh like let's say you wanted a an icon or something to to potentially be there for your message. You could add a slot that called icon or something and it may or may not have an icon in it, but that's a customization option where the webc provide like a fallback content of like, you know, if you don't provide an icon, I'll provide one for you, but then you can override it with your own one. >> Yeah. Yeah. There's lots of different things, but parts CSS properties are all and like attributes and things are all different ways that authors can provide config, you know, options that are baked into the component that are, you know, usable by consumers. I guess what the question that leads me to is how do you if you're creating a web component, how do you decide if something should be a part or if it should be done with custom properties or if it should just be something that's in the light that users then put in themselves. >> To be honest, it's really difficult to decide that. I think it's a function of just your sort of mentality for the component, what it's going to be doing, who your audience is, um how locked down you want it to be, like how important is it that the error message is red. If it is very important that it is red, then maybe you don't provide configuration options, right? But maybe it doesn't matter what color it is, and maybe it doesn't matter if it's got padding, things like that. Um, it it's all just sort of a function of what the author sort of needs and what the needs of the consumer base are. And um, so I I I tend to start like try try to like ride that line of it's a balance between flexibility and value because as a design system developer, right, um, my job is sort of to provide value out of the box. And in order to provide value out of the box, that kind of means making some stylistic decisions sometimes. And so I might decide it's red, but then the second part of that is sort of being open to changes of if my community says, "No, I need to be able to configure this. I've got valid use cases." Then I can always push a new version of that component and give you a part or give you a property or something. We're always trying to ride that line between um it like if if if the component you're making is the most flexible it could possibly be, then you're probably getting in the way and you probably have too many like your API of properties and parts and things is too big, right? And you're not providing as much value as you could be. So, but making those decisions is is a point of inflection where it's like, well, maybe I don't want it to be read all the time. So, there's always that balance of and it's kind of hard to decide. So, sort of use your judgment and know your audience in kind of decision. >> Yep. Perfect. Okay. That may because as we're going through it, I was just like I could see how I wouldn't know which one to do. My my instinct is I would feel like if like things like the custom properties make sense to me in terms of like these are as you said sort of like the API, these are the pieces you're allowed to touch whereas as soon as you do a part you're sort of handing control over. Uh so in certain again there's times where it be like oh yeah okay this makes sense for here but in my mind I probably would be using this the most. Um and then I guess the other thing that's hard to decide is when is something actually a part of the web component like our span is versus letting the user bring in the pieces lightdom instead. And there are two. I guess it completely depends on the use case of how the user is using it. Because in this case, I'd like this form group to work no matter what the person's styling looks like. So it makes sense that they can just class form group and style it how they want or just on the label the input however they're doing things and then we're automating the error messages primarily. So keeping this is userdefined and then the error message handling and all of that is happening through the web component. depends what the purpose of the web component is and everything else. >> Exactly. And I think that's something that like web component authors that do this a lot are constantly running up against when it comes to like browser APIs. Like when you talked about the handing over control um that we're kind of like it's either all nothing or just a little tiny bit like there's there's not very like um there's not very many ways to like hand directed control of these things to a consumer. So take take take a slot for example. There's no way for me to say that a slot should only really have icons in it. A slot is >> any HTML anything. So like the component might be designed for an icon, but when a consumer uses it, they can put 45 paragraphs of text and completely break it. But there's no way for me to as the author to say like don't allow things that aren't icons, like don't allow things that aren't this element, right? Um the same is true like you said of parts and of CSS variables, right? There's no way for me to say, you know, um this CSS variable is there for changing color, but I kind of only want reds. Like I kind I don't I don't really want pink. I don't I don't really want white. Like in this case, we had white. Like white is totally doable here. Like the technology will let you, but it's a terrible user experience because you now you can't see the error message anymore. Like if I'm building a component, I'm always like trying to figure out, okay, well, if I make it just hardcoded red, then I know that it'll be accessible on the background that I know about, but then you put this on a a brown background or something in your app and then h it's broken and you can't change it, right? So there's always this trade-off of, you know, flexibility and value and um all or nothing, right? and and we just sort of like it all comes down to sort of documentation of this is a slot which means you can put 45 paragraphs of text in it but please only put an icon like stuff like that. >> Yeah. No, it makes a lot of sense. Um yeah, I'm really happy with that. I learned a lot. Uh was there anything else that we haven't covered yet or did we did we get through it all? >> I think just to say congratulations. I think you've written your first web component. Is it is this your first web component that you've written? Basically, yeah, I've played around with some stuff before, but nothing that it's been sort of peacemealed from from other things. So, this is the first time I've actually sat down and like authored it from the beginning. Uh, so yeah, pretty happy with that. >> These APIs are very low level, but I think that's part of their power. And so, they're a little bit clunky, but once you sort of get get used to them, uh, the ability to provide sort of framework agnostic, you know, pieces of encapsulated functionality, behavior, style, and template is is amazing. >> Yeah. >> Awesome. Um, if people do want to learn more about web components, they're sold on them. Uh, what are some good resources? Uh, do you have any things they could follow uh for them to learn more? >> Oh, man. Um, that's one I think one of the cool things about it is that like it's literally MDN. It l it's literally just the browser the the browser documentation itself. You can sort of look up what's shadow roots. You can look up, you know, the connected callback hooks and things. You can look up all these things and learn about slots and learn about uh adopted stylesheets and all that. Um, it it's there's probably a trade-off there, too, because there's no such thing as the standard way to write a web component. And so you you can go look for, you know, bloggers and influencers and and and you technical folks out there, and everybody's going to tell you sort of how to do it differently. Um, and so like there's a beauty in that you can write it however you want to and sort of do it however you want, but there's also a struggle because you're not there's not one definitive way that everybody agrees agrees on. U, but MDN is your friend. you'll sort of learn anything about those. Um there's like there's lots of different like great libraries that you can sort of use as inspiration. So like web awesome uh and shoelace are like uh are great libraries that you can just sort of go and look and see how those things are done. All the code is open source and you can just sort of see oh what choices are they making and why and um you know some of those things um there's lots of good uh like web component libraries for bigger companies. you can go look at Adobe Spectrum, you know, like or um there's any number of different sort of web component like design systems nowadays uh that that are able to go at least look at the doc site, maybe not the source code, but you can check out sort of the choices that they're making. Um but yeah, >> cool. Yeah. Well, thank you so much for for walking me through all that. I really appreciate it. Learned a lot along the way and uh yeah, thank you for your time. >> Awesome. Thanks for having me. It was a blast. >> Yeah. And for everyone else, thank you for watching. I hope you really enjoyed it as well. And of course, until next time, don't forget to make your corner of the internet just a little bit more awesome.
Original Description
A big thank you to Michael Warren for creating and then walking me through using this web component!
🧑💻The web component: https://github.com/kevin-powell/form-groups-wc
🔗 The resources he mentioned:
✅ Lit: https://lit.dev/
✅ Web Awesome: https://webawesome.com/
✅ Shoelace: https://shoelace.style/
✅ Adobe's design system Spectrum: https://spectrum.adobe.com/
Follow Michael:
✅ https://michaelwarren.dev/
✅ https://bsky.app/profile/michaelwarren.dev
✅ https://www.linkedin.com/in/mwarren1106
✉ Keep up to date with everything I'm up to https://www.kevinpowell.co/newsletter
💬 Come hang out with other devs in my Discord Community https://discord.gg/nTYCvrK
⭐ Are you a beginner? HTML & CSS for absolute beginners is for you: https://learn.kevinpowell.co
🎓 Start writing CSS with confidence with CSS Demystified: [https://cssdemystified.com](https://cssdemystified.com/)
🚀 Already mastered CSS? Check out my advanced course, Beyond CSS: https://www.beyondcss.dev/
---
Help support my channel
👨🎓 Get a course: https://www.kevinpowell.co/courses
👕 Buy a shirt: https://cottonbureau.com/people/kevin-powell
💖 Support me on Patreon: https://www.patreon.com/kevinpowell or through YT memberships: https://youtube.com/@KevinPowell/join
---
🧑💻 My editor: VS Code - https://code.visualstudio.com/
🌈 My theme: One Dark Pro Var Night
🔤 My font: Cascadia Code
---
⌚Timestamps
00:00 - Introduction
03:02 - Creating Custom Elements
06:11 - Understanding Shadow DOM
08:54 - Lifecycle Hooks in Web Components
12:03 - Event Listeners and Memory Management
14:45 - Implementing Validation Logic
17:50 - Error Handling and User Feedback
36:13 - Defining Properties and Error Handling
39:10 - Customizing Error Messages
49:42 - Styling Web Components
01:00:18 - Enhancing User Experience with CSS Parts
01:10:06 - Resources for Learning Web Components
I'm on some other places on the internet too!
If you'd like a behind the scenes and previews of what's coming up on my YouTube channel:
Watch on YouTube ↗
(saves to browser)
Sign in to unlock AI tutor explanation · ⚡30
Playlist
Uploads from Kevin Powell · Kevin Powell · 0 of 60
← Previous
Next →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
How to create an awesome navigation bar with HTML & CSS
Kevin Powell
Improve your CSS by Keepin' it DRY
Kevin Powell
HTML & CSS for Beginners Part 6: Images
Kevin Powell
HTML & CSS for Beginners Part 7: File Structure
Kevin Powell
HTML & CSS for Beginners Part 4: Bold and Italic text and HTML comments
Kevin Powell
HTML & CSS for Beginners Part 5: Links
Kevin Powell
HTML & CSS for Beginners Part 3: Paragraphs and Headings
Kevin Powell
HTML and CSS for Beginners Part 1: Introduction to HTML
Kevin Powell
HTML and CSS for Beginners Part 2: Building your first web page!
Kevin Powell
HTML & CSS for Beginner Part 8: Introduction to CSS
Kevin Powell
HTML & CSS for Beginners Part 9: External CSS
Kevin Powell
HTML & CSS for Beginners Part 10: Divs & Spans
Kevin Powell
HTML & CSS for Beginners Part 11: Classes & IDs
Kevin Powell
HTML & CSS for Beginners Part 12: The CSS Box Model - Margin, Borders & Padding explained
Kevin Powell
HTML & CSS for Beginners Part 13: Background Images
Kevin Powell
HTML & CSS for Beginners Part 14: Style Text with CSS
Kevin Powell
HTML & CSS for Beginners Part 15: How to style links
Kevin Powell
HTML & CSS for Beginners Part 16: CSS selectors and Specificity
Kevin Powell
HTML & CSS for Beginners Part 17: How to Create and Style HTML Lists
Kevin Powell
HTML & CSS for Beginners Part 18: How Floats and Clears work
Kevin Powell
HTML & CSS for Beginners Part 19: Colors with CSS - hex, rgba, and hsla
Kevin Powell
HTML & CSS for Beginners Part 20: How to center a div
Kevin Powell
HTML & CSS for Beginners Part 21: How to create a basic website layout - the HTML
Kevin Powell
HTML & CSS for Beginners Part 22: How to create a basic layout - the CSS
Kevin Powell
How to Create a Responsive Website from Scratch - Part 1: The HTML #Responsive #HTML5
Kevin Powell
How to Create a Responsive Website from Scratch - Part 2: The Header and Hero area #Responsive #CSS3
Kevin Powell
How to Create a Responsive Website from Scratch - Part 3: The About Section #Responsive #CSS
Kevin Powell
How to Create a Responsive Website from Scratch - Part 4: Building a Responsive Portfolio Section
Kevin Powell
How to Create a Responsive Website from Scratch - Part 5: Call To Action and Footer #CSS #Responsive
Kevin Powell
Tutorial: Learn how to use CSS Media Queries in less than 5 minutes
Kevin Powell
End of the year upate and what's coming to my channel to start the new year
Kevin Powell
Create a CSS only Mega Dropdown Menu
Kevin Powell
CSS Tutorial: Outline and Outline Offset
Kevin Powell
CSS Blending Modes
Kevin Powell
Parallax effect | 2 different ways to add it with jQuery
Kevin Powell
CSS Units: vh, vw, vmin, vmax #css #responsive #design
Kevin Powell
How to Create a Website - Complete workflow | Part 01: Intro + Setting things up
Kevin Powell
100 Subscribers speed coding bonus video
Kevin Powell
How to Create a Website - Complete workflow | Part 02: The Markup #HTML
Kevin Powell
How to Create a Website - Complete workflow | Part 03: Sass Variables and a Mixin #Sass
Kevin Powell
How to Create a Website - Complete workflow | Part 04: Setting up the hero and header
Kevin Powell
How to Create a Website - Complete workflow | Part 05: Typography & Buttons
Kevin Powell
How to Create a Website - Complete workflow | Part 06.1: Building the navigation with Flexbox
Kevin Powell
How to Create a Website - Complete workflow | Part 06.2: Making the nav work with jQuery
Kevin Powell
Redesigning & Coding My Website #CreateICG
Kevin Powell
How to Create a Website - Complete workflow | Part 07: Starting the flexbox grid
Kevin Powell
How to Create a Website - Complete workflow | Part 08: Promo & Problem shooting!
Kevin Powell
How to Create a Website - Complete workflow | Part 09: The CTA and Footer
Kevin Powell
How to Create a Website - Complete workflow | Part 10: Making it responsive
Kevin Powell
How to Create a Website - Complete workflow | Part 11: Making it responsive con't
Kevin Powell
How to Create a Website - Complete workflow | Part 12: Putting the site online
Kevin Powell
Create a Custom Grid System with CSS Calc() and Sass
Kevin Powell
CSS em and rem explained #CSS #responsive
Kevin Powell
Should you use Bootstrap?
Kevin Powell
How to add Smooth Scrolling to your one page website with jQuery
Kevin Powell
Let's learn Bootstrap 4
Kevin Powell
How I approach designing a website - my thought process
Kevin Powell
Build a website with Bootstrap 4 - Part 1: The setup
Kevin Powell
Build a website with Bootstrap 4 - Introduction
Kevin Powell
Build a website with Bootstrap 4 - Part 2: Customizing Variables
Kevin Powell
Related Reads
Chapters (12)
Introduction
3:02
Creating Custom Elements
6:11
Understanding Shadow DOM
8:54
Lifecycle Hooks in Web Components
12:03
Event Listeners and Memory Management
14:45
Implementing Validation Logic
17:50
Error Handling and User Feedback
36:13
Defining Properties and Error Handling
39:10
Customizing Error Messages
49:42
Styling Web Components
1:00:18
Enhancing User Experience with CSS Parts
1:10:06
Resources for Learning Web Components
🎓
Tutor Explanation
DeepCamp AI