One Simple Trick to Cleaning Up Tailwind CSS Code

It's no secret that Tailwind CSS can make your markup very messy, but we can take some steps to prevent this. Probably the most common is to delegate classes to the parent element.

Take the following example.

<ul>
  <li class="text-sm font-medium text-gray-900">...</li>
  <li class="text-sm font-medium text-gray-900">...</li>
  <li class="bg-gray-100 text-sm font-medium text-gray-900">...</li>
</ul>

Here we have three repeating classes on the <li> elements.

  • text-sm
  • font-medium
  • text-gray-900

This can be helped by moving these classes to the parent <ul> element and have the CSS cascade down.

<ul class="text-sm font-medium text-gray-900">
  <li>...</li>
  <li>...</li>
  <li class="bg-gray-100">...</li>
</ul>

Boom! HTML cleaned up.

However, what if we are using classes that cannot be delegated to the parent?

Classes such as whitespace-nowrap, px-8, rotate-3 and many more, cannot be applied to child elements from the parent through cascading.

However, there's a solution...

Enter Tailwind CSS JIT

If you're not too sure what JIT is then here's a brief description from my How to Create Custom Gradients in Tailwind CSS with JIT blog.

Since v3, JIT has been the default in Tailwind CSS and has bought a lot of power to the framework. One of the best additions are arbitrary values, these allow you to replace custom CSS with Tailwind CSS like classes.

Let's use this example.

<ul>
  <li class="whitespace-nowrap p-4 text-sm font-medium">...</li>
  <li class="whitespace-nowrap p-4 text-sm font-medium">...</li>
  <li class="whitespace-nowrap bg-gray-100 p-4 text-sm font-medium">...</li>
</ul>

Here we have four repeating classes.

  • whitespace-nowrap
  • p-4
  • text-sm
  • font-medium

However, there are only two that can be delegated to the parent.

  • text-sm
  • font-medium

Here's how it would look without JIT.

<ul class="text-sm font-medium">
  <li class="whitespace-nowrap p-4">...</li>
  <li class="whitespace-nowrap p-4">...</li>
  <li class="whitespace-nowrap bg-gray-100 p-4">...</li>
</ul>

As you can see we still have p-4 and whitespace-nowrap repeated on all the <li> elements.

Delegating Classes with JIT

Here's how the example looks with JIT.

<ul class="text-sm font-medium [&>*]:whitespace-nowrap [&>*]:p-4">
  <li>...</li>
  <li>...</li>
  <li class="bg-gray-100">...</li>
</ul>

This might look like confusing but it's simple as [&>*] is just CSS.

It's the same as writing this.

& > * {
  //
}

Which translates to.

Select all first level children within this element

In our example the & is the <ul> and the * is the <li> elements.

Here's how that would look in CSS.

ul > li {
  //
}

And from that all we're doing is applying CSS but with Tailwind CSS utilities.

ul {
  font-size: 0.875rem; // text-sm
  line-height: 1.25rem; // text-sm
  font-weight: 500; // font-medium
}

ul > li {
  white-space: nowrap; // whitespace-nowrap
  padding: 1rem; // p-4
}

Here's a more built-out example using a table component from HyperUI.


I'm on the fence about this approach, as arbitrary classes have some downsides.

  • Confusing to read
  • Confusing to write
  • Messy markup

This post is called "One Simple Trick to Cleaning Up Tailwind CSS Code" and yet a downside of arbitrary classes is "messy markup"

It's more of an opinion. I initially thought arbitrary classes looked messy, but when used correctly they can really help clean up your Tailwind CSS code.

And of course, they're optional. There's no need/requirement to use them.