A taste of Tailwind

A taste of Tailwind

Get acquainted with Tailwind's utility-first approach by building a custom message component.

Video resources

Summary

Let's start by building this small piece of UI from the Discord app right here, this message component. And I'm going to start off in this blank app. This happens to be a React app, but don't worry if you've never used React before – the lessons we're covering apply to any tool or framework. The biggest difference is that we use className instead of class in React.

Okay so to start, we can see that Discord has this kind of darker theme to it. So let's just start by making the background of our app black. Now we can do this using the bg-black utility class from Tailwind:

export default function Home() {
  return <p className="bg-black">Hello, Tailwind!</p>;
}

And just like that, we can see our paragraph text is black. Now we can't read our text anymore, so let's go ahead and make our text white. And right away we see the styles change.

export default function Home() {
  return <p className="bg-black text-white">Hello, Tailwind!</p>;
}

So this is really the heart of Tailwind. It's these little utility classes that we apply directly to our markup to style our app without having to write any of our own custom CSS.

Let's keep going and we'll make this a min-height of the full screen so it takes up all this space:

export default function Home() {
  return (
    <p className="min-h-screen bg-black text-white">Hello, Tailwind!</p>
  );
}

Let's update this tag to be a div and let's give it a display of flex with items-center and justify-center:

export default function Home() {
  return (
    <div className="flex min-h-screen items-center justify-center bg-black text-white">
      Hello, Tailwind!
    </div>
  );
}

So Tailwind utilities are composable, which just means that each one of these classes does just one thing. And if we pop open our DevTools, we can actually see that right here. We see that the flex class applies the display: flex CSS rule, items-center applies align-item: center, and so on. And this is a really handy way just to see what each one of these classes is actually doing as you learn Tailwind.

Colors

Okay, the background over in Discord is not pure black, so we can come back and change this to a gray. Now Tailwind actually comes with lots of colors out of the box. There's grays, blues, greens, and reds, and for each color there are actually many shades. So for grays, we have gray-900 as the darkest gray, and then we go all the way down to gray-100 as the lightest gray, with even a 50 for just a touch lighter. And in our case, let's go with 700 for our background.

export default function Home() {
  return (
    <div className="flex min-h-screen items-center justify-center bg-gray-700 text-white">
      Hello, Tailwind!
    </div>
  );
}

Now you can see this gray doesn't match Discord's exactly. Later in the series we'll talk about customizing these colors so we can get a perfect fit, but for now we'll stick with the defaults.

Using the spacing scale

Okay, let's take another look at this message component. We see it has an image, a name, and a date, and then the actual message. So let's start with the image.

I'll come over here and let's drop in an image tag of Adam:

<div className="flex min-h-screen items-center justify-center bg-gray-700 text-white">
  <img src="/adamwathan.jpeg" alt="" />
</div>

So we can see the image tag being rendered, but it's a little bit big and we can use some Tailwind utilities to control the sizing. If we set the width to four and the height to four, we'll see this makes the image pretty tiny:

<img className="h-4 w-4" src="/adamwathan.jpeg" alt="" />

And if we inspect this, we can see that this ends up being a 16 x 16 image.

This 4 comes from Tailwind's default spacing scale, which is in rems. And rems are a best practice to use as a unit in CSS. But we can see that there's a 1:4 ratio between the Tailwind spacing scale and the 16 pixels that our image ends up being right here.

So, if we want to know the final pixel value of any of these sizes, we just multiply it by four.

If we pop over to Discord and inspect this image, we can see that it is 40 x 40 right here. So 40 divided by 4 is 10, which means we should just be able to set this to a width and height of 10, come back to our app, and now we'll have a 40 x 40 image.

<img className="h-10 w-10" src="/adamwathan.jpeg" alt="" />

Finishing the header

The next thing about this avatar is that it's rounded, and we can achieve that in our app using the rounded class:

<img className="h-10 w-10 rounded" src="/adamwathan.jpeg" alt="" />

Now, you'll see by default rounded just adds a little bit of board radius to the image, but there is a rounded-full class that will make it a complete circle:

<img className="h-10 w-10 rounded-full" src="/adamwathan.jpeg" alt="" />

And now our image is looking pretty good!

Now let's add a new paragraph tag and we'll add "adamwathan" as the name, and 01/15/2021 as the date:

<img className="h-10 w-10 rounded-full" src="/adamwathan.jpeg" alt="" />
<p>
  <span>adamwathan</span>
  <span>01/15/2021</span>
</p>

And let's style these. First we can give the name here a color of text-green-500. We also want to add just a little bit of space here to the right of the image. So we can come to our image and add margin-right of 4 on the spacing scale, and we'll also add a little bit of margin to the right of the name:

<img
  className="mr-4 h-10 w-10 rounded-full"
  src="/adamwathan.jpeg"
  alt=""
/>
<p>
  <span className="mr-2 text-green-500">adamwathan</span>
  <span>01/15/2021</span>
</p>

Now let's also make the name a little bit smaller and we'll make the date extra small. And if we look at Discord, we can see the date is a little bit lighter, so let's just soften this text a little bit. We'll make this text-gray-500.

<img
  className="mr-4 h-10 w-10 rounded-full"
  src="/adamwathan.jpeg"
  alt=""
/>
<p>
  <span className="mr-2 text-sm text-green-500">adamwathan</span>
  <span className="text-xs text-gray-500">01/15/2021</span>
</p>

Okay, and let's wrap the image and the text in a new div, and we'll make this a flex container as well, and the paragraph will be flex items-baseline, so that everything's nice and tidy up at the top.

<div className="flex">
  <img
    className="mr-4 h-10 w-10 rounded-full"
    src="/adamwathan.jpeg"
    alt=""
  />
  <p className="flex items-baseline">
    <span className="mr-2 text-sm text-green-500">adamwathan</span>
    <span className="text-xs text-gray-500">01/15/2021</span>
  </p>
</div>

One thing you'll notice when working with Tailwind is that it doesn't really abstract away CSS at all. So if you want to lay out your UI elements using Flexbox, you have to know how Flexbox works in order to use it with Tailwind. And this is a good thing because the more you use Tailwind, the better at CSS, you'll actually become!

Okay, so one last thing on our heading here, let's make the name a little bit thicker by changing the font-weight using font-medium:

<p className="flex items-baseline">
  <span className="mr-2 text-sm font-medium text-green-500">
    adamwathan
  </span>
  <span className="text-xs text-gray-500">01/15/2021</span>
</p>

And I think that looks pretty good.

Styling the message text

So now we can grab the text of this first message, come back to our app and let's drop it right below this heading here:

<p className="flex items-baseline">
  {/* ... */}
</p>
<p>
  You should never use something like leading relaxed with a big font size, it goes against all typography best practices. Line height should decrease as font size gets bigger
</p>

Okay we want these to be on top of each other, so let's wrap this in a div. And let's also wrap all of our content in a new div with a className of max-w-large:

<div class="max-w-lg">
  <div className="flex">
    <img />
    <div>{/* ... */}</div>
  </div>
</div>

Now again, if we flip over to Discord, we can see that the text is not pure white, it's a little bit softer than that. So we can come down to our paragraph and make it text-gray-300:

<p className="text-gray-300">
  You should never use something like leading relaxed with a big font size,
  it goes against all typography best practices. Line height should
  decrease as font size gets bigger
</p>

All right, let's grab the next message. Now, in Discord, conceptually you can see as we hover this, we highlight the full width here for each message. So conceptually, these are different things. So we're gonna copy this and we're gonna put it in an entirely new container that is a sibling to this first image and paragraph wrapper.

So back in our code, that would be this div right here, we'll drop a new div with a paragraph and our second message:

<div class="max-w-lg">
  <div className="flex">
    <img />
    <div>{/* ... */}</div>
  </div>

  <div>
    <p>
      You can override it in your config if you want but ultimately we
      chose the defaults they did because they let you get results closest
      to what a professional designer would do more easily
    </p>
  </div>
</div>

Let's also make this one text-gray-300. And you can see it's not aligned. Now, one nice thing about the fact that Tailwind uses the spacing scale for things like width and margins is that we can just add these up.

So we know this image is with 10, and it has a margin-right of 4 on it. So this total space right here is 14. So we should be able to come down to our new paragraph and add padding-left of 14 and get it perfectly line up:

<div class="max-w-lg">
  <div className="flex">
    <img />
    <div>{/* ... */}</div>
  </div>

  <div>
    <p className="pl-14 text-gray-300">
      You can override it in your config if you want but ultimately we
      chose the defaults they did because they let you get results closest
      to what a professional designer would do more easily
    </p>
  </div>
</div>

Now, we can also come to this wrapper div right here, and go ahead and add a margin-top of 1 just to give it a little bit of space, because we can see in Discord, there's just a little bit of space in between messages:

<div class="max-w-lg">
  <div className="flex">
    <img />
    <div>{/* ... */}</div>
  </div>

  <div className="mt-1">
    <p className="pl-14 text-gray-300">
      You can override it in your config if you want but ultimately we
      chose the defaults they did because they let you get results closest
      to what a professional designer would do more easily
    </p>
  </div>
</div>

Now let's go ahead and grab this last message, come back, and we'll just duplicate this and put in our last message:

<div class="max-w-lg">
  <div className="flex">
    <img />
    <div>{/* ... */}</div>
  </div>

  <div className="mt-1">
    <p className="pl-14 text-gray-300">
      You can override it in your config if you want but ultimately we
      chose the defaults they did because they let you get results closest
      to what a professional designer would do more easily
    </p>
  </div>

  <div className="mt-1">
    <p className="pl-14 text-gray-300">
      Since we changed this in tailwind 2 I’ve almost never used a leading
      class at all
    </p>
  </div>
</div>

Applying the hover treatment

Now I'll zoom out just a little bit here and let's go ahead and apply this hover treatment. So when we hover a message, we get a slightly darker background. And in Tailwind, we can do that pretty simply using the hover: prefix.

So remember, our app is currently a background of gray-700. Let's make it a background of gray-800 when we hover a message:

<div class="max-w-lg">
  <div className="flex hover:bg-gray-800">{/* ... */}</div>

  <div className="mt-1">{/* ... */}</div>

  <div className="mt-1">{/* ... */}</div>
</div>

And now we can see that new background is working.

It's a little dark though, and Discord actually uses a background opacity to lighten it up. So we can actually do the same thing with Tailwind using background-opacity, and let's say 30%:

<div class="flex hover:bg-gray-800 hover:bg-opacity-30">{/* ... */}</div>

And that looks pretty good.

Now if we pop over to Discord, we can see there's a little space on the side and above each message for the background. So let's come back over to our container, add some padding in the X direction of space 4, and just a little bit in the Y direction of space 1:

<div class="flex px-4 py-1 hover:bg-gray-800 hover:bg-opacity-30">
  {/* ... */}
</div>

And let's grab these classes and apply exactly the same treatment to our other message containers:

<div class="max-w-lg hover:bg-gray-800">
  <div className="flex px-4 py-1 hover:bg-gray-800 hover:bg-opacity-30">
    {/* ... */}
  </div>

  <div className="mt-1 px-4 py-1 hover:bg-gray-800 hover:bg-opacity-30">
    {/* ... */}
  </div>

  <div className="mt-1 px-4 py-1 hover:bg-gray-800 hover:bg-opacity-30">
    {/* ... */}
  </div>
</div>

And now we can see all of our messages have the hover treatment, and this is looking really good.

So, we ended up with a really nice reproduction of some custom UI here, and we didn't write a single line of CSS! No new files to maintain, no custom naming pattern that we had to come up with or explain to our coworkers. And this is one of the biggest value propositions of Tailwind. We just get to use all these shared utilities directly in our markup.

This is absolutely one of my favorite things about this library because how you break up your HTML and your templates depends so much on your team and what technology you're using. And with Tailwind, all your styling code lives right here in the markup, and just comes along for the ride.

So that's it for this quick intro, and I'll see you in the next video.