Compose Tailwind Utilities with @apply

Adam Wathan
InstructorAdam Wathan
Share this video with your friends

Social Share Links

Send Tweet
Published 4 years ago
Updated 3 years ago

The @apply directive lets you compose utility classes so you can re-use common styles and not litter your html with a ton of repetitive styles.

Even when using @apply directives, it's still easy to start repeating yourself in your code. We'll use the multi-class component pattern to extract the common styles into a base class and then styling (e.g. color) can be additionally applied as another class.

Instructor: [0:00] One hesitation you might have when working with utilities this way is that you end up with a lot of classes in your HTML. This could lead to potential maintenance issues if you need to update the same combination of utility classes in many places at once.

[0:13] I would argue that a lot of what we're doing here like setting the max width for some content or assigning a height to an image is pretty context dependent. There aren't really many useful obstructions we could introduce here beyond what we already have.

[0:25] Then there's things like this link down here that we have styled like a button. This has something like 15 classes on it. Buttons are the sort of thing that you could easily imagine yourself wanting to reuse in many places on the site.

[0:37] To avoid potential duplication issues here, Tailwind provides this special applied directive which you can use to compose a group of utilities into a reusable component class. I'll show you how it works.

[0:47] The first thing I'm going to do is select all of the classes in this long class list here, and head over to our tailwind.css file. Next, I'm going to create a new class in between Tailwind components and Tailwind utilities. I'm going to call it .btn, for button, like the button class from Bootstrap that you might be familiar with.

[1:05] I'm going to type @apply, which is Tailwind's special apply directive, here. Then, we're going to paste the entire list of classes that we copied from our HTML. Apply doesn't currently support using utilities that have variant prefixes, like sm, hover, or focus or anything like that, so I'm going to cut those out for now. I will talk about how to tackle those in a minute. Let's delete all of these hover styles, active styles, and focus styles.

[1:30] Next, let's go ahead and rebuild our CSS. Since we're going to be working with our CSS quite a bit in this lesson, instead of manually rebuilding it over and over again, why don't we add a quick watch script so that our CSS will automatically rebuild anytime we make any changes.

[1:44] Here in our package.json file, I'm just going to duplicate our build script. I'm going to call it watch, and then I'm just going to add the --watch flag to the end. This is just a feature of postcss-cli that will handle all the file watching for us.

[1:58] If we head over to the terminal, we should be able to run npm, run watch, and have a nice watch script running that'll automatically rebuild our CSS anytime we tweak it in any way.

[2:08] Let's check our build-css file. If we head over to public tailwind.css and we do a search for .btn, you can see that we have this new button class that contains all of these styles, that you might recognize as being all of the styles that would be added by the utilities that we applied to this component class.

[2:27] It works a lot like a mixin that you might be familiar with from Sass, except instead of mixing in things that are explicitly declared as mixins, you're able to mix in existing utility classes. Let's go ahead and add back our hover and focus styles and stuff like that.

[2:40] Here in our tailwind.css file, all we need to do is declare a few more selectors. I'm going to declare button hover, I'm going to declare button focus, and I'm going to declare button active. We can use apply inside each one of these selectors to reapply the styles that we were using under those specific conditions.

[3:00] For hover, we want to apply BG indigo 40 to make the background color a little bit lighter. On focus, we wanted to apply outline none and shadow outline. On active, we wanted to apply BG indigo 600 to make the background color a little bit darker.

[3:19] For changing the text size at the small breakpoint, you might think that we'd have to write something like media min width 640 pixels, which is the breakpoint that we're targeting, and then say button apply text base.

[3:32] This will totally work, but it introduces this magic number into your CSS. If you ever customize this breakpoint in any way, you'd have to come back to your CSS and manually replace every instance of this value.

[3:43] To make this a little bit easier, Tailwind provides the add screen directive, which lets you reference Tailwind's breakpoints by name. We can just say screen SM. When this is compiled, Tailwind will automatically expand this into the correct breakpoint.

[3:56] Let's go and check out our compile CSS, we can see this in action. You can see now we have our hover styles, our focus styles, our active styles, and the expanded breakpoint declared.

[4:06] Now that we've got all these styles in place, we can go and remove all the utility classes from the HTML and replace them with simply BTN. Now you see over here, now that everything's refreshed, everything still works the same. Our hover styles work. Our active styles work. Our focus styles work. Everything's looking good.

[4:23] One thing that I think is important to talk about a little bit is why we made sure to write these rules in between Tailwind components and Tailwind utilities. The reason for this is to make sure that even when you're using a class like .btn, you can still layer utility classes on top of it if you needed to tweak it in any way.

[4:40] For example, say we want to give this button a little bit of extra horizontal panning like PX8, because of the fact that we have button declared before the utilities, we can easily add PX8 here, and it'll actually take. You can see, the button actually gets wider.

[4:54] If we decided to put these button declarations after Tailwind utilities, even after adding PX8, this PX5 definition would override the PX8. You wouldn't be able to add any utilities that tweak any of the styles that are baked into the button glass.

[5:10] On that note, I think there are a couple things that we do have baked into this button class that might make more sense to leave to utilities. The first one that stands out to me is this media query here. I don't know that you'd always want to change the font size of every single button on your site whenever the viewport got bigger.

[5:25] I think this is the sort of thing that you might want to do on some buttons, but not on others. Why don't we delete this definition here and reintroduce it as a utility in our HTMLs? We'll say small text base. Similarly, this shadow here, I don't know that we want to have a shadow on every single button on the site either. It might be something that we add when we want to emphasize a particular button. I'm going to remove shadow-lg here and reintroduce this as a utility as well.

[5:51] One thing to be careful of when using the apply feature with tailwind is making sure you're not accidentally trading one type of duplication for a different type of duplication. I'll show you what I mean. Say we needed another button on this screen, maybe a secondary button where we had some text like learn more.

[6:08] Instead of being a primary indigo button like this, we wanted it to be a secondary gray button or something. Let's head over to our CSS and create another variant of this button. You might think, "OK, let's duplicate all these button styles, and maybe we'll rename this first one to be button indigo. Let's change the second definition to maybe be button gray."

[6:31] Instead of making this one have an indigo background with white text, maybe this is a gray-400 background with a gray-800 text. Then for the hover state, maybe that gets a little bit lighter, so gray-300. For active, it gets a little bit darker, so say, gray-500.

[6:48] Let's head over to our HTML and make the necessary changes. We'll change this first one to be button indigo and the second one to be button gray. I think this button actually looks bad with the shadow, especially since it's a secondary button.

[7:02] We can remove this utility and feel validated that extracting the shadow from the button was probably a good idea. Looking at these buttons, it seems like everything's good. They both have their own styles. Everything seems to be working. The classes are nice, simple, and extracted. No problems.

[7:18] Say we wanted to change the padding on both of these buttons. We'd have to go to our CSS file and edit the padding for both the button indigo class and the button gray class. This is what I mean by trading one form of duplication for another.

[7:32] We've extracted these component classes, but there's actually duplication within them that can create different types of maintenance issues. To solve this, we can use what's often referred to as a multi-class component pattern.

[7:43] Here's what I'm going to do. I'm going to create a new class up here called button, reintroducing this base button class. I'm going to copy this apply directive. I'm going to remove anything from it that's very specific to this indigo variant. In this case, I think that's only the background color here and the text color.

[8:02] Let's take a look at the button indigo definitions here and see if there's anything else that's common between both variants. The thing that stands out to me is the focus styles. We're doing outline-none, shadow outline on both of these.

[8:13] Why don't we also add some focus styles to our base button? We'll get rid of indigo here, keep this as it is. Now we can go through the indigo variant and the gray variants and remove anything that's already covered by this button class.

[8:26] First, I'm going to remove these focus styles. I'll remove that from both variants. Then we have to remove everything except the colors. We can remove all of the block and positioning stuff, we can remove the text size, and we can remove all this stuff at the end.

[8:40] All we really need to specify here is indigo-500 for the background and white for the text. We can do the same thing for this gray button, keeping the colors. Now we have very simple definitions for these two variants. You can see it's very clear what's different between them, because the only things we specify are things that aren't the same.

[8:58] If we head back to our index.html file, all we have to do is re-add this button class to both links, and now we're back to where we started. Apply is a cool feature of tailwind that can be really useful when you want to extract repeated combinations of utility classes, but I would caution you not to get carried away.

[9:16] My experience is most useful for things like buttons or form elements, the sort of thing that are single HTML tags and things that you repeat many times throughout your site. For anything that's more complicated than a single HTML tag, there's a better way to handle potential duplication problems. We'll talk about that in the next lesson.

Stephen James
Stephen James
~ 4 years ago

Do you have vs code fix for the errors I see for tailwind directives @tailwind "Unknown rule @tailwind"

Adam Wathan
Adam Wathaninstructor
~ 4 years ago

What I do is use the postcss language mode for my CSS files instead of the css language mode and I don't get any warnings like that.

Markdown supported.
Become a member to join the discussionEnroll Today