Creating Dynamic Style Variants
Let's start this time by defining the shape of the objects that we want to create.
Taking advantage of the autocomplete that using TypeScript gives us, we can create these objects quickly.
Instructor: [0:00] Let's start this time by defining the shape of the objects that we want to create. For the sizeClasses, we need to cover the large, medium, and small variants. Let's start with large, set an empty string, medium, and small. The TypeScript error is gone.
[0:16] For the shapeClasses, we're going to cover square, rounded, and pill. You can see that without having to refer to the ButtonProps type, I can use the autocomplete suggestions and easily create the object shape that is required to make sure we cover every possible variant.
[0:35] Let's start with sizeClasses. I will scroll down to the size variants down here. What's affecting the size here is likely the font size and the internal padding inside the button. For the first one, px-7, py-2.5, and text-lg as well. I think everything else is out of the size concern. I'll copy these and paste them in the large variant.
[0:59] Going to the second one, the medium one, we have px-5 and py-2. We do not have a font size here, because I guess the font size is the base, the normal default font size. I'll paste the padding classes here.
[1:14] I think it's worth here explicitly specifying the font size with text-base, not only to communicate the intent, but also possibly reset the font size if a parent element sets the font size to something else. Here, I will specify text-base.
[1:28] For the third one, the small variant, we have px-3, py-1, and text-small. Let's try to blend these sizeClasses alongside the baseClasses and impactClasses. SizeClasses, and we'll reach for the dynamic size prop value. Hit Save. You can see our buttons are taking shape rapidly now. We can see the different sizes here working.
[1:54] Essentially, what's missing is the shapeClasses, the rounded corners, the pill button. Everything else seems to be falling in place really nicely. Good stuff.
[2:04] Let's go and implement the shapeClasses now. I'll continue going down for the shape variant. Here, we're most likely looking for just rounded corner classes. The square has no class. We can see here for the rounded, it has rounded-md.
[2:20] Once again, we could leave an empty string for the square variant, but let's explicitly set rounded-none which sets the border radius to zero pixel. We've seen that the rounded was using the rounded-md class. I'll paste that one here. The pill variant uses the rounded-full class.
[2:41] One more time, let's go in the className attributes. We're going to compose one more variant. The shapeClasses reaching for the dynamic shape prop. Our buttons now look really nice.
[2:56] Let's do a quick recap. We've taken a series of hardcoded buttons with a lot of repeated classNames for each button, and then essentially organized our classes within different style concerns. Then, we composed these parts together dynamically inside the className attribute, which allows us to organize dynamic styles in a really elegant and organized way.
[3:18] The baseClasses is a string that stores every utility class applied to all of our buttons. Then, we have our impactClasses for the bold, light, and none variant. You know what, I just realized that for the bold variant, we have a shadow here. We forgot to implement the disabled reset of that shadow.
[3:35] If we look at the bold hardcoded button, when the button is disabled, we undo that shadow. I'm going to copy this and add that one to the bold variant. These are our impactClasses, setting the background color, text color, and as you've seen, the shadow.
[3:52] I'll remove this comment here. Then, we have the sizeClasses, setting padding classes, and font size classes for the different variants. The shape classes do the same for the rounded corners.
[4:02] Once we have all these styles lookup objects put together, we compose them together in the className attributes. We have the baseClasses. Then, for each prop, we reach for the dynamic prop value passed for the impact, the size, and the shape.
[4:16] By doing so, each button gets the correct mix of Tailwind classes applied without having any complicated logic inside the className attributes. Instead, we have all our styles organized in one place, this Tailwind classes lookup directory.
[4:30] All of the classes used for all of our buttons are organized in one place, and we don't use any interpolation to create dynamic classes. Everything is going to work really well with the Just-In-Time engine.
[4:41] Let's scroll down here and play with it a little bit. I'm going to change the props passed to the first button here. It's this button right here. You can see it doesn't have any props, because it's using all the default values which is bold, medium, and rounded.
[4:55] I'll just change the text here to Pro Tailwind. Let's have some fun with it. I can change the impact. You can see the auto suggestion. Let's change it to light, maybe change the size to small, and the shape to pill.
[5:10] Let's make the impact back to bold by removing the prop. You can see that everything is working really well. If I add the disabled attributes, there is no point or events, no shadow, and everything is working really well.
[5:25] All our nine buttons are displayed here within these 15 or so lines of code. You can see that this is incredibly more pleasant to work with and maintain than what the hardcoded buttons look like when we're repeating all the Tailwind Classes.
[5:40] Hopefully, that gives you a good idea of how you can organize your styles and compose multiple style concerns together. Up to this point, there wasn't much complexity in our buttons.
[5:50] We always had objects with strings inside of them, but this approach of having style lookup objects can also handle more complex scenarios as we'll see in the next challenge.