 Welcome to another episode of GUI Challenges where I build interfaces my way and then I challenge you to do it your way because with our creative minds combined we're going to find multiple ways to solve these interfaces and expand the diversity of our skills. Cue in that intro. Today we are building a switch component which sounds so simple. I mean it is the smallest component that I think we've built on GUI Challenges so far and ironically it has the most to like compacted amount of nested complexity from CSS complexity to JavaScript complexity to state complexity. It was packed with all of these nested things I wanted to build so let me try to break these down one by one and show you what I built. First off I built mine on top of a checkbox so if I hit command shift I and just pop open the dev tools and look at this element here it looks like a label so I've bundled the label and the input checkbox which is being the switch together so there's no ambiguity between like what label and which switch is there and I've even given them the for and the id here so there's like an explicit relationship between their attributes and a relationship in the way that they're nested and that came in really handy for a couple of cool features which we'll cover later but it also meant I needed to be taking this checkbox very seriously there's a lot of states and a lot of interactions and a whole API that comes with it that I need to tie into and make sure that's working great so let me close that out. So we have the default state this is like a checkbox can be checked or unchecked right default it's not checked we have an indeterminate checkbox so JavaScript can load and set a checkbox to an indeterminate state of which it's like obviously not off or on it's waiting for you to make a choice and I just love how inviting that looks I think it looks nice I care on ah now now it's on okay so we have disabled off disabled checked we also have all of these in dark and light themes and we have a vertical button that can go up and down additionally it to the states of a checkbox there's a whole bunch of overrides and we got the overrides mostly for free because of the way that I establish the custom properties on this particular label element so those are all the states let's cover some of the different ways that you can interact with this element and to do that let's go to the debugging corner we've already seen me use the mouse to like hover and get an interaction we can click on the label we can click on an actual like thumbnail here and have it switch so there's like lots of ways to click and use our mouse but how about the keyboard what is the keyboard do well if I tab through you can see that I get a nice little focus offset so that's one style I put on there and the spacebar button will toggle these so by using a checkbox element we're automatically a focusable element and notice where like if we're disabled we're not a focusable element we're not in the focus in the like the the tab tree that's just so cool so we got that for free and we got this spacebar activation but what about uh screen readers what are screen readers going to see so let me hit command at five and I'll show you voiceover on safari and I'm going to hit tab to go to our next switch in determinant on switch that is exactly what it was and notice it says it's a switch not a checkbox default off switch default off switch i'm going to turn off screen reader voiceover off the way that we got that announcement as a switch instead of a checkbox is by putting the role equals switch on the element and I go over this in the article a little bit more okay so we've talked about mouse we've talked about keyboard we talked about screen reader we need to talk about touch so notice I have safari in a tablet over here now so we now have firefox in the bottom left chrome here chrome on android ios safari um mac os safari and tablet safari here and I just uh I need these sometimes the tablet layout is really important to me to get the feel and the layout right and so I'm going to include that now in my debugging corner as one of these views that I want to be looking into my my layouts and my interactions here and what's nice about having an iPad here is I can demonstrate that it is indeed touch friendly to a pointer all right so we can use our pointer across android and ios we can drag these nice ones here like that so lots of really great touch interactions so we're not just building something for a mouse and maybe a keyboard we're covering all of the different ways that someone's going to want to hit this switch and making sure that they get a nice experience and while we're talking about some of these experiences with like a mouse like notice that the hover on well here let's go to the bigger bigger screen here notice when I hover on a label it uh like activates the appropriate switch you can then hover on the switch still see that hover feedback what I wanted to show here those if I hit command option I right if I'm clicking these so if I hover it scales up that animation and if I click that switches back and forth in a nice animation but if I go here to reduced motion reduce and we might as well prefer color scheme light here while we're here now if I hover look at that I get an instant scale up so I'm still providing the feedback that that's the appropriate switch for this interactive element and if I hit the button I get an instant switch back and forth so there's no motion but I do animate the color there see how the color is still animating and I think that's nice that's just really good visual support even if someone has reduced motion requests that we're building a really robust very feedback oriented switch right okay cool stuff I also want to showcase how this supports right to left so if I pop up in here I can go to the direction and set right to left and hit enter and I want to point out some really important things let's actually look at left to right before we go do that notice in left to right the off state is to the left all right let me go take a reduced motion let's go yeah right here yeah the off state is on the left which is inline start and the on state like the active state is inline end I made that the same thing in right to left so in right to left you can see that the inline start state is off and the inline end state is still on nice right so these are some of the considerations that I had to think about when building something right to left and left right is like where should the thumb be and what sort of interaction am I looking for here but also I like then right to left we got this like swap of sides for free just by using flexbox and check this out if I go over to the code let's pop up in here to the head section I'm going to get rid of the JavaScript I'm going to get rid of the CSS and reload what we have is a still working switch the default switch works in determinant still works disabled the vertical the vertical switch doesn't really look any different but it's still a switch nonetheless and I think that's really interesting and if we bring the styles back in all we really do is change the way that that whole interaction looks it's like this complete visual upgrade now the JavaScript in here this is see look I can't click and drag this anymore the JavaScript is entirely to add that drag interaction so now I can drag this button back and forth so I like the way that that that layered in we have this solid semantic HTML set that represents a toggleable you know state and then we can upgrade it with a CSS file and if we want we can bring in the JavaScript file again so it's like a very resilient to very upgradable component that was all based on the the fact that this markup was sturdy from the beginning another fun interaction here is how did I do the vertical variant so we have the variant vertical down here so like switches don't always come in a vertical state and this one it ended up being kind of like half for free it was like a really interesting thing I was like hmm I wonder if I could support vertical just by like rotating it so what we do at least what I set up here is we have a modifier class like if I pull up in the class here if I get rid of this vertical modifier we can see that the vertical modifier class when used with the GUI switched class just changes the switch because we have scenarios down here where we're going to want like a vertical layout but with a horizontal switch and in this one we wanted a vertical layout and a vertical switch so let's check out if we have that vertical class present we'll bring down that class toggle UI we'll come to the input and I can see that there's a rotation being applied here let me pull this open so this rotation looks a little less scary but what we're doing here is we're taking the rotation which is we want to do this on 90 degrees so this is just like a kind of like a quarter turn right one fourth of a turn and we're multiplying it by a variable called is left or right and we'll talk about that in a second because that's how I handled right to left languages using transforms which don't have a logical value like we can't do you know translate logical inline we did that just doesn't exist yet we only have x and y so I have to do that work myself but what's cool here is that rotation was all that we needed to get that element totally working in a verticality and if I go like here here's an angle right go to angle state I can still drag and click my switch I thought that was so cool so I'm just using this rotation component like go to another angle and rotate and drag it around and it's just so neat that this has no idea that that even exists so I'll go ahead and close that angle rotation up I just thought that was neat that we achieved it with a rotation and that rotation ended up persisting all of the interactions and I didn't have to write anything different in my logic especially for the drag like look I can drag at an angle and I get that for free I thought that was really neat oh I don't really get it for free I got it because of my choice of my stack so just a cool feature a fun little demo to showcase that I can rotate and still make this entire thing interactive which means you could have uh yeah 45 degree angle switches if you want that I don't really know or care and plus say look that one kind of looks cool like that right looks nice okay let's talk about a few of the layouts that we have in here I'm gonna go back to the dark theme dark theme if you're wondering how I did the color theme I have a color scheme episode and GUI challenges and I definitely used all the techniques inside of there so here's my HTML you can see all of my colors here on the HTML and then depending on like dark or light I just set some of those colors it's really really straightforward and it's a pattern I'm really having a lot of fun with so let's talk about the layouts of these elements here so we have a label well here let's check out this like default one just the basic switch and let's pull this out a little bit bigger here great so here's our label it only comes with a few styles it comes with a display flex a line items center which keeps all of them vertically centered and space between by default with a little bit of gap that means that we can even close this down like look at this if I crunch it see how there's still a gap here there's still a little gap right there so these things will never touch but they'll also spread into the space if they're made available and I think that's just because most of the switches that I see have a layout like this and so that's the default layout and that's why I show in the overrides that you can stack them you can put them at the end you can do all sorts of different combinations you're in control of that flex box layout you should feel empowered to sort of pop this open change directions right so here's everything stacked here's everything in the red regular direction and then you can change some of the alignments if you want so it's all overwriteable with just some additional css now the layout here for this actual switch was really cool too so this is using grid we can see our grid layout here I'm going to go ahead and turn that on let's turn on the flex one too just because this is all going to look really cool flex flex flex nice okay so this grid layout if we look closely over here it's saying it's track track so I've called this input checkbox which looks like a track uh I've given it a grid layout that's called track and the tracks are called track I don't know if you're really following my like funny little scenario here but it looks like a track there are tracks in grid and then I called them tracks and it just worked out really nice and that grid layout so it creates this single cell that then this before element the pseudo element can be on top of so here's with grid area track and that's really critical here because normally a pseudo element would need position absolute or something to sort of be on top of an element right and in html and css things go below usually they go the next block underneath but to get something on top I've used grid to find a cell and then I have something claiming that cell that just so happens to be on the track what's really cool about that too is that the position for checked and unchecked is zero percent and a hundred percent pretty much like this element is just fitting within the track and the math got really easy because of this layout so I thought the flex layout in combination with this grid layout made for a very modern you know non absolute positioned element solution that was really flexible and continued to look really nice and be really nice to work with so that's how those two layouts worked and of course in the article there's lots more css examples to go over you can go check those out it's a good time now to go over some of the overrides so if I come down here to the overrides I can look at just the inline html for a lot of these and describe like how they're overriding what is in here so we've got this one here which is called custom active color and we can look at the style here this is an inline style called track active hot pink and that's it so you've overwritten one custom property that I passed down to all the logic and we'll use that as the active color here kind of nice extra small is interesting all I'm changing is the thumb size to 15 pixels so I can make this smaller or larger and this thing doesn't know or care it's a very dynamic and adjustable element here how small does it go oh look it turned into a square that's funny okay so like two or three pixels it's the smallest thumb you're gonna be able to get there can you still grab it here if I make a one pixel thumb oh that's so funny I'm interacting with the world's tiniest switch it's a switch and it's so tiny okay anyway let's make that something more reasonable 15 pixels great the XL one okay just make a large thumb size we have that one of 50 pixels here's a custom track size so notice the track size is 8 rem and the thumb is still taking its thumb size from whatever it needs to be from the custom property but it all works out so everything is calculated into dynamic here and that makes that really nice that you can change the track size I mean if you want to to whatever you want by default the track size is twice whatever the thumb size is but again it's overwriteable you can also override this track padding which I thought was kind of cool here you get a very different looking button and in this case I made it so that the button track area fit that hover highlight really well and that just kind of turned out cool I like the way that that looked and maybe that's the style that you want on your switch you should be able to change that padding right maybe there's no gutter around the edge you want it to be edge to edge here's another one where we kind of stacking it so I'm changing the flex direction to column I've given it a vertical gap here instead of a much smaller gap than what's default and I flex aligned to start this other one here we have um display justify content flex ends that's how we're getting it to line to the end it's also taking that whole chunk of content and moving it over here's our label so this is the only one that has a wrapper element this label is then using flex direction column flex content flex start and then it's setting to inactive inactive colors so it's overwriting the different track colors so that's why we have off is red that's the inactive color and the active color is lime and we're getting this sort of on and off switch with red and green with just a few overrides look that's just a few overrides and the label since it's set to vertical it has these two different labels on top and bottom and they're centered and they're wrapping and it just doesn't know flexbox is just really powerful and a great way to to lay those out and this last one here I went a little bit more heavy on the changes here and customizations and look I gave it a class called iOS light and I tried to emulate something that was iOS like um but I gave it some a little bit more flare like if you've seen this like blue hover highlight I thought that was really nice so we have the blue active state and then the blue hover highlight and I'm doing that all here where I'm changing the flex direction to column right because we're getting a stacked label and the labels on top of the button we have a small gap we're again aligning items to flex start and then we're just setting some of these custom properties an active track an inactive track we're setting the thumb size the thumb color and the thumb highlight and we're getting that sort of blue highlight over top of a white uh switch that has a blue active state with just five overrides and like some custom layout and I thought that worked out really nice so that's how the overrides work and that's how the html looks and I think we got to cover some of the JavaScript don't we we have to look at some JavaScript okay follow me over to my code editor we go find all the elements with the class go we switch we create a map for them because we're going to stash each of these switches as a key in this map and then cache a bunch of values like the the width and the height of the different custom items that have been put on there so we're stashing all these dynamic attributes of these switches into cache into the state well just into this like switches map here and then we use that very often like through the drag and stuff like that like right here if we look at this element here um and we watch its inputs right here and as I drag see how the thumb position is being written constantly do do do do and the transition duration is set to zero that's so that while we're dragging we're not getting any interpolation we don't want to like ease our way through the drag we want to feel very attached to that element and the thumb position is rounding those things to absolute whole numbers and we're keeping track a lot of this stuff per element by using that switch that switch map that we're looking at right here so this has a few events that are really interesting we have drag and knit so that's as soon as you click into the thumb it will go create the listener for dragging so it's a very dynamic drag not everything is waiting for dragging all the time you have to sort of click in there first then we call the dragging event that's just going to watch your mouse move inside of that and outside of it right we can drag outside of it and inside of it here and pull way up top and let go and it's still going to be on and that's done down here we have this window add event listener pointer up so we have dragging it dragging drag end and that's the sort of core what's happening and then the sort of main crux of everything is when it when this script spins up it's going to go through each of those elements it's going to find their input which is the checkbox inside of there it's going to find the styles for the pseudo element right because those are dynamic they could have been customized there's some overrides we're going to go grab the padding off of that element as well so even though that's a custom property we need to go find those values because someone could have customized it and we want the computed value to we don't want one ram we want something like 16 pixels so then we add our event listeners for pointer down pointer up right here's dragging it drag end we're looking for a checkbox listener to prevent bubbles like if someone's clicking on this checkbox we don't want the label to also fire and if the switch which is the label gets clicked we're going to go handle that label click ourselves and then here's where our map sets all of those cached values we have our switches we set to this gooey switch so this current switch that we're iterating on here's a whole bunch of information to stash in memory about it we want to know its thumb size the padding and then the bounds for the track the lower bounds the upper bounds and then the where's the middle and we stash all that so that we can easily do computations during drag and don't have to ask for a bunch of expensive properties make sure to check out that article and I hope you learned something new and I hope you feel inspired by this switch you should feel like you can go grab this switch and use it in any project that you want to and I want to see what your switches look like what's important to you in your switches and I'll see you on the next gooey challenge y'all take it easy