Building Twitter's Dropdown Menu with Headless UI and Framer Motion

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey everyone sam here and today we're going to be building one of twitter's drop-down menus using headless ui and framer motion it was my first time using headless ui and i was really impressed with just how fast i got this menu built so i wanted to make sure and show you how to do it so let's get right into it so here's what we're going to be building today uh if you click on the share button on a tweet or even these three dots right here you'll see this little drop down menu has a little bit of animation uh you can navigate it using the arrow keys uh you can dismiss it with the escape key so this is exactly what we're going to be building and over here i have just a little twitter clone that we'll be able to use to build this thing so we can see right here we just have these buttons wired up right now but they're not actually doing anything and we can see the share icon right here so let's go ahead and wire up this with a drop down menu now i've already added headless ui react to this project so we can just get started and wrap this button in a menu component and we'll see menu is not defined so we can go ahead and auto import it from headless ui react and if we come up here we should see it right there so that's the first step in getting our menu working the next is to turn our button into a menu dot button so this is how these components from headless ui are exposed and then next is to render a menu.items component and we can put anything in here we want hello from the menu and now over in our ui if we click this button we see hello from the menu now it's kind of moving these buttons over because it's taking up space in the dom but if we come back we can style this menu items component just using class names so we can make it absolutely positioned and now if we try the menu again we'll see the menu shows up and it doesn't rearrange everything so let's go ahead and wrap this menu in a div with class name relative make this right zero and now our menu should be positioned a little bit better so we can come over and keep adding some classes to make this look more like a menu let's give it a background of white and a border maybe a shadow for now and let's also give it a z index so it shows up on top of the other ui all right now let's start rendering some items so the way we do this is using the menu.item component and we'll just steal the labels from the twitter ui so the first was send via direct message add tweet to bookmarks and copy link to tweet so we're seeing this error that says you should render an element as a child and that's because these menu.item components are actually render lists and this is where the name headlist comes from in headless ui as many of these components as make sense to will actually not render an element leaving the control up to you and that gives you a complete flexibility when it comes to how these things look so right now what we're going to do is come to all these three items and we'll just go ahead and wrap them in an a tag since these are really links and now we'll see that error went away and if i come click on the button we'll kind of see our links right here and this is great just because you know we know how to style anchor tags we can just throw a class name on here let's get rid of this wrapping and let's make them block elements as well we'll give them some padding in the x direction and the y direction and let's fix our items here give it a border of cool gray 200 as well as make it rounded okay so this is starting to look pretty good now the first thing you'll notice here is if i click one of these menus we actually see the focus ring on the menu itself that's an accessibility feature that comes with the library for free it's really neat you don't have to do anything but it actually helps screen readers focus on the element that was clicked and then you can see right here actually when i hit escape or click off the menu that the last button goes to focus as well so these are all these little things that come for free with this library again i was really impressed there's basically no setup no wiring context providers anything like that all this stuff just works so since we're kind of styling this ourselves we can go ahead and say focus outline none but the focus is still maintained there again for screen readers so that will work just fine now the next thing you'll notice is that uh my hover state here is not doing anything and you might think to start coming here and adding hover classes to your anchor tags with tailwind but with headless ui there's a better way these menu items actually accept a render prop and that means we can pass in a jsx expression here with the function that returns our element so if we save that we'll see our menu still shows up but this exposes some new apis one being the active property here and this is what we can use to actually style our links in the active state so let's go ahead and make this class name and expression we'll change these to backticks and now we can say if this link is active let's go ahead and give it a background of cool gray 200 otherwise we'll leave it blank and now if i come over and hover the menu you'll see that our link has a hover state so let's go ahead and use this on all three menu items and now if i come over and try this out we can see they're all hoverable but more than that if we were to open the menu and use our arrow keys we can see the active state is styled for those as well so that's really cool again all the stuff just come in for free and that's why we use the render prop so that the active state can be managed for us by headless ui now i didn't call this out but you'll notice if i hit escape here the menu goes away or i click outside to close the menu goes away and again the focus is restore but you can just see you know just how fast and easy this was it's really really neat now we're almost done actually you know for functionality we can come here and wire up links or on click handlers but as far as the ui goes this thing is looking pretty good with the exception of a little bit of animation so if we come back to twitter's ui and we try this out we'll see that there's a little drop down that happens right here and just a subtle little animation a little drop down and fade in so it'd be nice if we could do that on our side as well so to do that we're going to be using framer motion which is an animation library i like using for stuff like this you know the box is growing out and framer motion does all this cool stuff using flip technique to calculate the final size of the box so that you can just smoothly transition from height 0 to auto so when i see something like that that's kind of what i think so i'm going to bring this in and also it's going to show you just how well these libraries compose together again headless ui is really meant to be this low level flexible tool kit instead of giving you something out of the box that's hard to customize it it literally gives it up to you it gives rendering up to you to decide what you want to render and even what react elements you want to render as we're about to see so i've already installed framer motion and the easiest way you use it is just with a motion.div so if i were to go ahead and update this to motion.div we should get an undefined error here go ahead and auto import this from framer motion if i come up here we'll see that right there come back down and this takes an animate prop which we can just say something like opacity 1 and the initial is opacity zero and so now if i kind of reload this page we should see those buttons fade in and you can also pass in a transition prop with a duration of one second just so we can really see this and uh there you go now we don't actually want to transition in the button here so we'll update this back to just be a div but what we want to do is transition the menu items when we click on the button so because we need to animate this the first step here is to actually kind of take control of rendering and unrendering this items menu ourself instead of letting headless ui do it and that way we can apply our framer motion transition to it so what we're actually going to do is use the render prop from menu so menu exposes a render prop as well and we'll go ahead and wrap all this in just a fragment and the property here is open so this tells us whether the menu is open or not now the next trick is to use this static prop on our menu.items so remember we have our menu.items and here we have the three items but this is the actual like items list and if we throw a static prop on it and save we'll actually see that the menu is always showing kind of for every tweet here so static just means that it's always rendered and it's up to us to control whether or not it's showing and now we can basically just use this open prop to do that so we can just drop an expression in here and say if we're open then go ahead and render this items menu and so now we're kind of back to exactly where we started where if we click on the menu it toggles the open state and that open state now controls rendering for our uh items list here so this gives us a little bit more control and lets us interface with libraries like framer motion so right now this menu.items is just rendering a div with our classes but we can actually pass in an as prop and pass in any element here so we could pass in an unordered list or a main component or we can even pass in an expression and pass in motion.div so motion.div again is coming from framer motion but here we're just passing it in to our headless ui menu.items component and right now we're still going to get this menu showing just like before but we basically have inherited all of these cool animation properties from this motion component which means we should be able to do something like animate equals opacity 1 initial equals opacity 0. and now if we come over here look at that we got our fading menu it's a little slow let's go ahead and say transition equals duration and we'll just say 0.15 seconds feels a little bit better and now for the cool part let's say the initial height is zero and the final height is auto check that out just growing from zero to auto without us doing anything else we didn't even have to use any hooks or our own state or anything like that just a few render props and headless ui working really beautifully here with framer motion and us not really having to write a whole lot of code at all and i think we've ended up with something really close to what we see in the native twitter ui right here so just a little bit of fade and that growth and that's exactly what we have so this is pretty awesome and of course again we have the accessibility so if i were to use the tabs hit enter here i can switch my focus i can hit enter on this it brings up the menu i can hit escape or select and now i'm back focused on the right spot so again the accessibility features built in here not only was it easy but this is probably the most semantically correct drop down i've ever built in my entire career and i've been doing this for you know seven or eight years so the fact that it's both easier and more correct i think is a testament to how good this library is and if you've ever had to try to customize a jquery ui drop-down or any anything else like that you know how difficult that can be so i was really stoked with this i'll be sure to share the code for this project but hopefully you learned a thing or two and i hope you're excited about these projects like i am thanks so much for watching and i'll see in the next one
Info
Channel: Sam Selikoff
Views: 5,855
Rating: 4.9726963 out of 5
Keywords:
Id: BUAc-bkoJuw
Channel Id: undefined
Length: 13min 8sec (788 seconds)
Published: Wed Oct 28 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.