English

Hello! Have you tried or want to try Tailwind CSS? Perfect, I'll be sharing some important things about Tailwind CSS.

It's not exactly the 'best practices' either. It's more about coding style and some tips that make me comfortable using Tailwind CSS.

It's opinionated, but who knows, i hope something positive can be taken from it.


Coding Style

Here is the coding style for writing Tailwind CSS classes that I often use, divided into 3 important points: sorting, splitting and grouping.

Sorting

Try to take a look at the following Button component example, so many classess 😂

<button
  class="inline-flex h-10 items-center justify-center gap-1.5 rounded-md border border-transparent bg-purple-600 px-4 text-center text-sm font-bold text-white transition duration-150 hover:bg-purple-700 dark:bg-purple-600 dark:hover:bg-purple-500 md:rounded-xl"
>
  Button Text
</button>

Because dealing with a large number of classes for just single element, class sorting is quite important.

Fortunately, Tailwind CSS itself has provided the Prettier plugin to handle this matter.

Consistent class order will make the maintenance process easier in the future.

For example, in the sample, with the classes already sorted, we can easily change the border and border-transparent because they are next to each other.

Or easily change the hover: styles or breakpoints because they usually appear at the end of the class.

Splitting

To make it neater, we can split the class into several strings or lines. I usually use clsx for this.

An example of a Button component that has already used clsx:

function Button({ children }) {
  return (
    <button
      className={clsx(
        'inline-flex h-10 items-center justify-center gap-1.5 rounded-md border border-transparent bg-purple-600 px-4 text-center text-sm font-bold text-white transition duration-150 hover:bg-purple-700 md:rounded-xl',
        'dark:bg-purple-600 dark:hover:bg-purple-500'
      )}
    >
      {children}
    </button>
  );
}

Grouping

After the classes have been splitted, we can 'group' them to make it neater and more readable.

The pattern I usually apply is:

  • group 1: default utilities
  • group 2: breakpoint utilities
  • group 3: dark mode utilities
  • group 4: other variants utilities

Let's implement this into previous Button component:

function Button({ children }) {
  return (
    <button
      className={clsx(
        // group 1
        'inline-flex h-10 items-center justify-center gap-1.5 rounded-md border border-transparent bg-purple-600 px-4 text-center text-sm font-bold text-white transition duration-150',
        // group 2
        'md:rounded-xl',
        // group 3
        'dark:bg-purple-600 dark:hover:bg-purple-500',
        // group 4
        'hover:bg-purple-700'
      )}
    >
      {children}
    </button>
  );
}

Or with the CSS component:

.button {
  /* group 1 */
  @apply inline-flex h-10 items-center justify-center gap-1.5 rounded-md border border-transparent bg-purple-600 px-4 text-center text-sm font-bold text-white transition duration-150;
  /* group 2 */
  @apply md:rounded-xl;
  /* group 3 */
  @apply dark:bg-purple-600 dark:hover:bg-purple-500;
  /* group 4 */
  @apply hover:bg-purple-700;
}

Much better, I think. Groups 2, 3, and 4 can still be improved or adjusted to your preferences, just make sure to maintain consistency in writing.


Tips

Use Component

If you feel that some elements have same styles, they can be split into separate components, such as buttons.

Since I'm using React, I can create the component like:

function Button({ children }) {
  return (
    <button
      className={clsx(
        'inline-flex h-10 items-center justify-center gap-1.5 rounded-md border border-transparent bg-purple-600 px-4 text-center text-sm font-bold text-white transition duration-150',
        'md:rounded-xl',
        'dark:bg-purple-600 dark:hover:bg-purple-500',
        'hover:bg-purple-700'
      )}
    >
      {children}
    </button>
  );
}

Use Class Component

In a static site, classes are served as is. Therefore, if we have elements that use the same set of utilities multiple times, it will increase the size of our HTML file.

For example, see this repeated utilities on these pagination buttons:

<button
  class="inline-flex h-10 items-center justify-center gap-1.5 rounded-md border border-transparent bg-purple-600 px-4 text-center text-sm font-bold text-white transition duration-150 hover:bg-purple-700 dark:bg-purple-600 dark:hover:bg-purple-500 md:rounded-xl"
>
  Previous
</button>
<button
  class="inline-flex h-10 items-center justify-center gap-1.5 rounded-md border border-transparent bg-purple-600 px-4 text-center text-sm font-bold text-white transition duration-150 hover:bg-purple-700 dark:bg-purple-600 dark:hover:bg-purple-500 md:rounded-xl"
>
  1
</button>
<button
  class="inline-flex h-10 items-center justify-center gap-1.5 rounded-md border border-transparent bg-purple-600 px-4 text-center text-sm font-bold text-white transition duration-150 hover:bg-purple-700 dark:bg-purple-600 dark:hover:bg-purple-500 md:rounded-xl"
>
  2
</button>
<button
  class="inline-flex h-10 items-center justify-center gap-1.5 rounded-md border border-transparent bg-purple-600 px-4 text-center text-sm font-bold text-white transition duration-150 hover:bg-purple-700 dark:bg-purple-600 dark:hover:bg-purple-500 md:rounded-xl"
>
  3
</button>
<button
  class="inline-flex h-10 items-center justify-center gap-1.5 rounded-md border border-transparent bg-purple-600 px-4 text-center text-sm font-bold text-white transition duration-150 hover:bg-purple-700 dark:bg-purple-600 dark:hover:bg-purple-500 md:rounded-xl"
>
  4
</button>
<button
  class="inline-flex h-10 items-center justify-center gap-1.5 rounded-md border border-transparent bg-purple-600 px-4 text-center text-sm font-bold text-white transition duration-150 hover:bg-purple-700 dark:bg-purple-600 dark:hover:bg-purple-500 md:rounded-xl"
>
  Next
</button>

Imagine if in one HTML file there are several paginations as well, the size of the HTML file will definitely be bigger!

To reduce it, we can abstract or create CSS components instead. So, we can transform the element utilities to CSS component, like:

<button class="pagination-button">Previous</button>
<button class="pagination-button">1</button>
<button class="pagination-button">2</button>
<button class="pagination-button">3</button>
<button class="pagination-button">4</button>
<button class="pagination-button">Next</button>

!important Usage

Like vanilla CSS, try to avoid using !important.

In Tailwind CSS, !important can be added by adding the ! prefix to the utility class, for example, !p-2 !px-3.

Try to avoid it as much as possible! Once trapped into using it, all your utilities may end up with many !important and it will be very difficult to make style changes in the future.

Conditional Class

Don't
<div className={`text-black ${dark && 'text-white'}`}>
  Hello, World!
</div>
Do
<div className={dark ? 'text-white' : 'text-black'}>
  Hello, World!
</div>

Let me explain a bit.

If we use the first method and the value of dark is set to true, then the class would be text-black text-white.

In some cases, the result might be as expected. However, in some cases, it might cause a problem.

It's possible that the text remains black even when the value of dark is true.

Let the code speak:

<!-- Some elements within the project. -->

<div class="text-white">White!</div>
<div class="text-black">Black!</div>

<!-- 
  The above elements will generate CSS in the following order:

  .text-white {
    ...
  }
  .text-black {
    ...
  }
-->

<!--
  Imagine that "dark" is set to true, and use a class defined as follows for conditional class:
      class={`text-black ${dark && 'text-white'}`}
-->
<div class="text-black text-white">Still black? CSS Specificity!</div>

<!-- 
  That's because, in this example, both "text-black" and "text-white" have the same CSS properties and specificity.
  
  On the other hand, the last one defined in the CSS file (in the previously generated CSS) will take precedence!
-->

Tailwind Play