The styles aren't the only thing that can be duplicated - often times HTML structure is repeated in our applications. You don't want to just pull out the styles in a @apply directive.
This is where JavaScript comes in handy. We have the data available to us so we'll loop through that data and build repeated card components with Tailwind
Adam Wathan: [0:00] Behind the scenes, I've gone ahead and added a new section to our little splash page here that lists some popular destinations for people to take their workcation.
[0:08] The thing I want to direct your attention to is the destination cards here. We've got six of these cards here. If you go ahead and take a look at the code, you can see there's a lot of repetition.
[0:18] For example, the wrapper for the card says all these classes, and that's repeated six times. Here's the classes for the image, again, repeated six times. All the styles for all the links repeated six times.
[0:28] When you see all this duplication, you might be tempted to use Tailwind's apply feature to extract some classes. You might head up to this div up here and think, "OK, you know what, maybe we should have something like a destination-card class, and maybe this can be like a destination-card-image, and maybe here, we have like a destination-card-title."
[0:46] While this technically works and does solve some of the duplication problem, the list of utility classes repeated in the different elements of this component are really only a small part of what's actually been duplicated here.
[0:57] The thing that we're leaving out is all of the structure. It's really important that the image is on the left, and that this is div on the right, and that this title goes in this h-3 tag, and that we have a p tag underneath, and another div that wraps the a tag.
[1:10] No matter how many classes like this that you extract, you still have to repeat this entire HTML structure every time you want another instance of this destination card.
[1:19] There's a better way we can solve this problem. Let's pack out changes here, so we're back to everything working with utilities.
[1:25] Now, up until now, we've been doing all this work in just a plain simple vanilla HTML project. The reality is, if you're building a site like workcation that has all those dynamic data down here, you certainly aren't hard-coding in all of these different destinations into HTML.
[1:40] You're most certainly working with some framework like maybe Ruby on Rails, or Laravel, or React, or Vue, or even building a WordPress theme. When you're in an environment like that, you have a lot of power at your disposal that you don't have in a vanilla HTML template.
[1:54] To show you how I would solve this problem in a real-world project, I converted our existing project into a very simple Vue CLI app. Now, down here at the bottom of this file, you see that I have all of the data for all of these destinations loaded as just a simple array of JavaScript objects.
[2:11] Up here higher in the template where we have all these stuff hard-coded, if we have all these data available and we need to dynamically display it based on the state of the system, we wouldn't just be hard-coding in all this information, we'd be spitting all this stuff out in a loop. Why don't we convert this over?
[2:26] Here, we've got the very first entry Toronto here. I'm going to take this wrapper element here on line 29 which is the container for all this stuff, and I'm going to loop over all of our data so we can render one card for each city.
[2:39] I'm just going to say v-for="destination in popularDestinations" which is the name of the variable where we have all that data stored.
[2:48] Now over here on the right, you can see that we've got six more Torontos added to the top of the list. Let's walk through making all this data dynamic and actually pulling it from the data that we have stored in our Vue component.
[2:58] First things first is this image. I'm just going to change this to destination.imageUrl. Next thing I'd change this Alt tag to be destination.imageAlt. Down here, where we have Toronto, the city title, we can just change this to destination.city.
[3:18] Down here where we have the price, we can change this to destination.averagePrice. Finally, where we have the number of properties that you can explore, let's change that to destination.propertyCount.
[3:34] Now, you can see on the right here, we've got all this data being pulled in dynamically. Then we have all the hard-coded stuff still below it. Let's remove all the hard-coded stuff since we don't actually need any of that stuff anymore.
[3:44] We'll just find the end here -- I think this is probably right -- and see what we're left with. Now, if we go ahead and check everything on the right, you can see we just have the dynamic data.
[3:55] The thing that I think is interesting and worth pointing out here is all of a sudden, we don't actually have a duplication problem at all. This combination of utility classes only exists in one place, here on line 30. Same with this link, it only exists in one place, here on line 36.
[4:10] If you don't need to use one of these cards somewhere else on the site or somewhere else on this page, there's no longer any actual pressure to extract any extra CSS classes. You don't actually have any duplication.
[4:22] Say we did want to use one of these cards either somewhere else on this page or on another page on the site, how can we avoid the duplication then?
[4:29] In that case, the best way to create a single source of truth for this component is to actually create a component, either a JavaScript component if you're working in an environment like Vue, or React, or even a simple template partial if you're working in Rails, Laravel, or WordPress.
[4:44] Let's create a very simple Vue component here to show you what I mean. Over here in the sidebar, I'm going to create a new file in our components directory. I'm going to call it DestinationCard.vue. Now let's add a template section and a script section. Now, we can copy the template over from our other file in this loop.
[5:02] I'm just going to grab everything inside the loop because if you look at what's on the outside, this is really just specifying the shape of the columns and stuff. You'll see like on different screen sizes, this actually goes to a two-column layout, and then a three-column layout.
[5:16] We don't actually want to extract the columns. We just want to extract the cards inside the columns. Let's cut this stuff, head over to the DestinationCard, and paste this in, clean up our formatting a little bit here, and then to make sure that we can actually pass in the data needed for this component, in Vue, we need to define a prop.
[5:33] I'm just going to add a prop here called destination so we don't have to change our template.
[5:38] If we head back over to our main template here, we can import the destination card from ./components/DestinationCard, and make sure to register our DestinationCard component here. Up on line 30, all we need to do is drop that into our template passing through the current destination from the loop.
[6:01] As long as we haven't made any mistakes, we should be able to scroll down. Yeah, all our destination cards are here. If we wanted to reuse this destination card in a different place on the site, all we have to do is pull in this component.
[6:12] Again, now because the entire template has a single source of truth inside this component. There's still no pressure for us to extract any extra classes here. There's no duplication at all.
[6:23] If we wanted to change the border radius in every card, we'd just be able to come to this one file and change it to like rounded-none and, now all of a sudden, we have square corners, or rounded-sm and now we have small corners. This big long list of utility classes is no longer causing any maintenance issues at all.
I was expecting a Tailwind feature in this lesson, but "don't use Tailwind for a UI framework/library problem" is also a valid lesson in itself.