Reducing utility class duplication

One of the main concerns for Developers evaluating or starting with a utility-first approach to styling is maintainability due to the number and duplication of classes.

For example, with this component, as I duplicate it to add more links, I also duplicate the classes applied to the link - making it harder to maintain:

<li>
  <a class="block py-2 border-b-2 border-transparent hover:text-blue-500 hover:border-blue-500 focus:bg-yellow-400 focus:border-current focus:outline-none" href="#0">About</a>
</li>

There are some solutions to this, which you can also see in an example Astro project at https://github.com/opdavies/reducing-utility-class-duplication.

Extracting a variable of class names

The simplest way to remove the duplication is to create a variable that holds the class names which can be reused. For example, Astro allows variables to be created within the frontmatter section and used within the template. Creating variables can be done in other templating engines too.

---
const linkClasses =    "py-2 block border-b-2 border-transparent hover:border-blue-500 hover:text-blue-500 focus:outline-none focus:bg-yellow-400 focus:border-current";
---

<li><a class={linkClasses} href="#0">About</a></li>
<li><a class={linkClasses} href="#0">Talks</a></li>
<li><a class={linkClasses} href="#0">Blog</a></li>

Extracting CSS components

Instead of extracting a variable, you could also extract a CSS component. With Tailwind, the @apply directive within a stylesheet will create a reusable CSS component:

<Layout>
  <ul
    class="flex flex-col gap-4 justify-center sm:flex-row sm:flex-wrap sm:gap-6"
  >
    <li><a class="link" href="#0">About</a></li>
    <li><a class="link" href="#0">Blog</a></li>
    <li><a class="link" href="#0">Talks</a></li>
  </ul>
</Layout>

<style>
  .link {
    @apply py-2 block border-b-2 border-transparent hover:border-blue-500 hover:text-blue-500 focus:outline-none focus:bg-yellow-400 focus:border-current;
  }
</style>

The link class can be added to any link items, and whilst the approach works, I prefer to use template-based solutions and keep the classes within the HTML markup.

Loops and maps

Templating engines will have a way to loop over a list of items. This can be used to remove the duplication and only have a single list of classes whilst keeping the benefits of keeping them in the HTML code.

For example, in Astro:

---
const links = [
    { name: "About" },
    { name: "Blog" },
    { name: "Talks" },
    { name: "Daily list" },
    { name: "Search" },
];
---

{links.map(link => (
  <li>
    <a class="block py-2 border-b-2 border-transparent hover:text-blue-500 hover:border-blue-500 focus:bg-yellow-400 focus:border-current focus:outline-none" href="#0">
      {link.name}
    </a>
  </li>
))}

Extracting HTML components

Finally, if the component needs to be reused, it can be extracted into its own file and re-imported where needed.

This is the extracted component:

---
const { name } = Astro.props;
---

<a class="block py-2 border-b-2 border-transparent hover:text-blue-500 hover:border-blue-500 focus:bg-yellow-400 focus:border-current focus:outline-none" href="#0">
  {name}
</a>

And the original file:

---
import Layout from "~/layouts/Layout.astro";
import Link from "~/components/Link.astro";
---

<Layout>
  <ul class="flex flex-col gap-4 justify-center sm:flex-row sm:flex-wrap sm:gap-6">
    <li><Link name="About" /></li>
    <li><Link name="Blog" /></li>
    <li><Link name="Talks" /></li>
    <li><Link name="Daily list" /></li>
    <li><Link name="Search" /></li>
  </ul>
</Layout>

This can now be used anywhere within this project and could be combined with the loop approach, passing the appropriate value to the name prop.

Summary

All of these approaches remove duplication, either using features provided by Tailwind CSS or a templating engine, to make the code more maintainable.

I've shown Astro in these examples but the same can be done with PHP, Twig, Blade, Vue, React, etc.

The examples are public on GitHub at https://github.com/opdavies/reducing-utility-class-duplication/tree/main/src/pages.

- Oliver

P.S. Do you want to learn about automated testing in Drupal? Take my free 10-day email course and get daily lessons straight to your inbox.

Was this useful?

Sign up here and get more like this delivered straight to your inbox every day.

About me

Picture of Oliver

I'm an Acquia-certified Drupal Triple Expert with 17 years of experience, an open-source software maintainer and Drupal core contributor, public speaker, live streamer, and host of the Beyond Blocks podcast.