Tailwind CSS Tips and Tricks Worth Knowing
Tags: css, tailwindcss, tips, webdev
Published: 2023-07-17 08:25:37

In this blog post, I’m not going to say why you should use Tailwind. I’ve covered the whole debate enough [in a previous post](https://www.builder.io/blog/the-tailwind-css-drama-your-users-don't-care-about).

This time I’ll explore some Tailwind tips that can significantly enhance your web development experience. Whether you're a beginner or an advanced Tailwind user, I hope you find something useful.

Let’s go!

## Dynamic utility classes

Tailwind purges classes that are not being used. This is how it's able to have so many features and still keep our CSS bundle size small. So, if you want to use dynamic class names, you need to have all the class names you want written somewhere in your code. This is in order for Tailwind to be able to statically analyze your code.

For example something like this won’t work:

```ts
const DoesntWork = () => {
  const colors = ['red', 'green', 'yellow'];
  const [color, setColor] = React.useState(colors[0]);
  const changeColor = () => {
    setColor('green');
  };
  return (
    <>
      <div className={`w-40 h-40 border bg-${color}-500`}></div>
      <select
        value={color}
        className={`bg-${color}-500`}
        onChange={(e) => setColor(e.target.value)}
      >
        <option value="">choose</option>
        {colors.map((c) => (
          <option key={c} value={c}>
            {c}
          </option>
        ))}
      </select>
      <button onClick={changeColor}>Change color</button>
    </>
  );
};
```

That is because there is no way for Tailwind to find it’s classes statically. Having `bg-${color}-500` needs to be evaluated in runtime. However, if we do use the full class names, the Tailwind compiler can make it work:

```ts
const Works = () => {
  const colors = ['bg-red-500', 'bg-green-500', 'bg-yellow-500'];
  const [color, setColor] = React.useState(colors[0]);
  const changeColor = () => {
    setColor('bg-green-500');
  };
  return (
    <>
      <div className={`w-40 h-40 border ${color}`}></div>
      <select
        value={color}
        className={`${color}`}
        onChange={(e) => setColor(e.target.value)}
      >
        <option value="">choose</option>
        {colors.map((c) => (
          <option key={c} value={c}>
            {c}
          </option>
        ))}
      </select>
      <button onClick={changeColor}>Change color</button>
    </>
  );
};
```

## Using Tailwind inside CSS

There are some times where we are forced to use CSS for our styles; for example, when using a third-party library. We can stick with the Tailwind colors by using the `@apply` directive or the `theme` function. Let’s have a look at a code example:

```css
.__some-external-class {
  /* Using @apply we can use any utility class name from Tailwind */
  @apply text-blue-300 bg-gray-300 py-2 px-6 rounded-lg uppercase;

  /* or using the theme() function */

  color: theme('colors.blue.300');
  background-color: theme('colors.gray.300');
  padding: theme('padding.2') theme('padding.6');
  border-radius: theme('borderRadius.lg');
  text-transform: uppercase;
}
```

### Arbitrary values

Another way to write pure CSS inside Tailwind is with brackets (`[]`). This is what is referred to as “arbitrary values”. You can do things like this:

```html
<div class="w-[100vw] bg-[rebbecapurple]"></div>
```

What more is that you can use the theme function as well:

```html
<div class="grid grid-cols-[fit-content(theme(spacing.32))]">
  <!-- ... -->
</div>
```

In case you want to reference a [CSS custom property](https://developer.mozilla.org/en-US/docs/Web/CSS/--*), there’s no need to use the `var` keyword (since v3.3). You can simply pass in your CSS variable as an arbitrary value:

```html
<div class="bg-[--my-color]">
  <!-- ... -->
</div>
```

## Group and peer utility classes

Tailwind allows us to change the style of an element based on its state with helper classes such as `:hover`, `:checked`, `:disabled`, `:focus`, and more (you can [find them all here](https://tailwindcss.com/docs/hover-focus-and-other-states#quick-reference)). So it’s easy for us to do something like this:

```html
<button class="bg-purple-500 border border-blue-500 text-white text-2xl uppercase p-6 rounded-md m-4 transition-colors hover:bg-purple-800 hover:border-blue-200 hover:text-gray-200">Click me!</button>
```

The result would be the below:

<iframe src="https://codepen.io/hamatoyogi/embed/preview/WNYOVgy?default-tabs=html%2Cresult&amp;height=300&amp;host=https%3A%2F%2Fcodepen.io&amp;slug-hash=WNYOVgy" style="top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;" allowfullscreen></iframe>

What if we want to change the style based on the state of another element? This is where the peer and the group utility classes come in handy.

### Style based on parent state

For instance, we can change the style of child elements when the parent is hovered by turning the parent into a group and using `group` and `group-hover:` utility classes:

```html
<div class="relative rounded-xl overflow-auto p-8">
  <a href="#" class="group block max-w-xs mx-auto rounded-lg p-4 bg-white ring-1 ring-slate-900/5 shadow-lg space-y-3 
     hover:bg-sky-500 
     hover:ring-sky-500">
    <div class="flex items-center space-x-3">
     <svg class="h-6 w-6 stroke-sky-500 group-hover:stroke-white" fill="none" viewBox="0 0 24 24">
      <!-- ... -->
     </svg>
     <h3 class="text-sm text-slate-900 font-semibold group-hover:text-white">New project</h3>
    </div>
    <p class="text-sm text-slate-500 group-hover:text-white">Create a new project from a variety of starting templates.</p>
 </a>
</div>
```

Which would result in the following:

<iframe src="https://codepen.io/hamatoyogi/embed/preview/GRwEVOr?default-tabs=html%2Cresult&amp;height=300&amp;host=https%3A%2F%2Fcodepen.io&amp;slug-hash=GRwEVOr" style="top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;" allowfullscreen></iframe>

There are more helper classes to modify the child elements and this works for almost every pseudo-class modifier (here’s [the full list](https://tailwindcss.com/docs/hover-focus-and-other-states#pseudo-class-reference)).

### Style based on sibling state

The `peer` class modifier can be used to style an element based on the state of it’s sibling. You can use the `peer-{modifier}` where `{modifier}` can be any pseudo-class modifier.

Here’s a simple example:

```html
<div class="flex flex-col items-center gap-20 p-10 bg-pink-400">
 <p class="peer cursor-pointer">I am sibling 1</p>
 <p class="peer-hover:text-white">I am sibling 2</p>
</div>
```

When we hover over “sibling 1” the text will change the “sibling 2” element:

<video muted autoplay loop playsinline controls>
      <source src="https://cdn.builder.io/o/assets%2FYJIGb4i01jvw0SRdL5Bt%2F0928b358dbe54db1904607f512827268%2Fcompressed?apiKey=YJIGb4i01jvw0SRdL5Bt&amp;token=0928b358dbe54db1904607f512827268&amp;alt=media&amp;optimized=true" type="video/mp4">
    </source></video>
### You can name names

Both with the `group` and `peer` you can give unique names to differentiate groups and peers.

This is done by adding `/{name}` to either helper classes, for example:

```html
<div class="group/main w-[30vw] bg-purple-300">
 <div class="group/hello peer/second h-20 w-full flex flex-col items-center justify-around">
  <p class="group-hover/hello:text-white">Hello Wolrd!</p>
  <p>All this code belogs to us</p>
 </div>
 <div class="peer-hover/second:bg-red-400 w-[200px] h-[200px] bg-blue-300">
 </div>
 <div class="group-hover/main:bg-green-200 peer-hover/main:bg-red-400 w-[200px] h-[200px] bg-orange-300">
 </div>
```

## Register a Tailwind component to Builder

You can register components from within your code straight into Builder.io’s Headless Visual CMS and allow non-devs to drag and drop your custom components in the Builder UI.

To do so, [you need to have a Builder account](https://builder.io/signup) first. Then, [follow the docs to get set up](https://www.builder.io/c/docs/quickstart).

Once you have finished setting up and you’ve connected to Builder, create a component in your repo using Tailwind.

Let’s assume you’re working with Next.js. We can create a new component:

```ts
// src/components/card.tsx

export const Card = ({ text }: { text: string }) => {
  return (
    <div className="relative rounded-xl overflow-auto p-8">
      <a
        href="#"
        className="group block max-w-xs mx-auto rounded-lg p-4 bg-white ring-1 ring-slate-900/5 shadow-lg space-y-3 hover:bg-sky-500 hover:ring-sky-500"
      >
        <div className="flex items-center space-x-3">
          <svg>
           {/* ... */}
          </svg>
          <h3 className="text-sm text-slate-900 font-semibold group-hover:text-white">
            New project
          </h3>
        </div>
        <p className="text-sm text-slate-500 group-hover:text-white">{text}</p>
      </a>
    </div>
  );
};
```

Then we [register the component to Builder](https://www.builder.io/c/docs/custom-components-setup):

```ts
// [...page].tsx

// Register this component for use in the Visual Editor
Builder.registerComponent(Card,{
  name: 'Card',
  inputs: [
    // 'name' is the name of your prop
    { name: 'text', type: 'text' },
  ],
)
```

And then we can just drag and drop our component into our app via the Builder UI, as below where the left side is Builder and the right side is our connected Next.js app:

<video muted autoplay loop playsinline controls>
      <source src="https://cdn.builder.io/o/assets%2FYJIGb4i01jvw0SRdL5Bt%2F7666cb6aa5e94fe1b47aecc9ee753028%2Fcompressed?apiKey=YJIGb4i01jvw0SRdL5Bt&amp;token=7666cb6aa5e94fe1b47aecc9ee753028&amp;alt=media&amp;optimized=true" type="video/mp4">
    </source></video>
## Animation utility classes

Tailwind has some very useful and easy-to-use animation utility classes. For example, we can add the transition color class and set a duration of 300 milliseconds to create a smooth color change effect on hover. We can also pass an animation curve and a delay for the animation:

```html
<div class="... hover:bg-gray-300 transition-colors duration-300 ease-in-out" />
```

Almost any animatable property is available to you (for a full list [see here](https://tailwindcss.com/docs/transition-property)).

Other than that, there are premade animations available like: `animate-spin`, `animate-ping`, `animate-bounce`, and `animate-pulse`.

## Responsive designs

Tailwind is a mobile-first framework, which means that un-prefixed utilities take effect on all screen sizes, while the prefixed utilities override the styles at the specific breakpoint and above. This helps write your CSS mobile first, as you need to define from small the larger screens.

Let’s say we want a grid of images or videos. We want our design to be one column on mobile, and then on larger screens be 2 columns, and on desktop have 3 columns, like so:

<video muted autoplay loop playsinline controls>
      <source src="https://cdn.builder.io/o/assets%2FYJIGb4i01jvw0SRdL5Bt%2F2ea579d23b21448f8457c963bb4ff1fc%2Fcompressed?apiKey=YJIGb4i01jvw0SRdL5Bt&amp;token=2ea579d23b21448f8457c963bb4ff1fc&amp;alt=media&amp;optimized=true" type="video/mp4">
    </source></video>

This is how we would write it:

```html
<div class="grid grid-cols-1 gap-10 p-5 sm:grid-cols-2 md:grid-cols-3">
 <div class="w-full aspect-video bg-cyan-500"></div>
 <div class="w-full aspect-video bg-cyan-500"></div>
 <div class="w-full aspect-video bg-cyan-500"></div>
 <div class="w-full aspect-video bg-cyan-500"></div>
 <div class="w-full aspect-video bg-cyan-500"></div>
 <div class="w-full aspect-video bg-cyan-500"></div>
</div>
```

Custom `min` and `max` utility classes are available as well for more dynamic use cases. Furthermore, you can add custom breakpoints into your `tailwind.config.js` configuration file.

## Editor extensions

The [Tailwind CSS Intellisense extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) for your IDE is one of the main reasons why Tailwind is so pleasant to use. It auto-completes the class names for you, shows you the color being used, and explains the details of the class when you hover over it.

<video muted autoplay loop playsinline controls>
      <source src="https://cdn.builder.io/o/assets%2FYJIGb4i01jvw0SRdL5Bt%2F9a1ca64e3d9e4af2a80502cc8916a2f1%2Fcompressed?apiKey=YJIGb4i01jvw0SRdL5Bt&amp;token=9a1ca64e3d9e4af2a80502cc8916a2f1&amp;alt=media&amp;optimized=true" type="video/mp4">
    </source></video>

Other than that, you can get Prettier sorting your classes with the [Tailwind Prettier plugin](https://github.com/tailwindlabs/prettier-plugin-tailwindcss). And one more quality-of-life extension that might help your eye sores from a long list of classes is [Tailwind Fold](https://marketplace.visualstudio.com/items?itemName=stivo.tailwind-fold).

## Creating custom utility classes

We can use the Tailwind configuration file to create our own custom utility classes. This is very useful if we want to use a specific style in multiple places in our app. So, if we want to add another box shadow class for example, this is what we’d need to do:

```js
// tailwind.config.js

module.exports = {
  content: ['./src/**/*.{html,js}'],
  theme: {
    extend: {
      boxShadow: {
        // Note that we can use the theme function in here as well
        neon: "0 0 5px theme('colors.purple.200'), 0 0 20px theme('colors.purple.700')"
      }
    }
  },
}
```

Then we could use it in our code:

```html
<div class="w-20 h-10 rounded shadow-neon"></div>
```

Anything in Tailwind can be extended or overridden.

## Creating custom Tailwind plugins

If we want to be able to choose the color of a custom utility by passing the color, we need to make our own custom Tailwind plugin. This is a bit advanced, but it allows us to create very flexible and reusable utilities.

Let’s reuse our neon shadow example. To add that ability to it, we can go to `tailwind.config.js` and define our plugin:

```js
// tailwind.config.js

const plugin = require('tailwindcss/plugin');

module.exports = {
  content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
  theme: { // ... our previous config },
  plugins: [
    // to get the colors we can use the "theme" property 
    plugin(({ theme, addUtilities }) => {
      const neonUtilities = {};
      const colors = theme('colors');
      
      // loop through the colors 
      for (const color in colors) {
        // Check if color is an object as some colors in
        // Tailwind are objects and some are strings
        if (typeof colors[color] === 'object') {
          // we opt in to use 2 colors 
          const color1 = colors[color]['500'];
          const color2 = colors[color]['700'];
          
          // Here we build the actual class name
          neonUtilities[`.neon-${color}`] = {
            boxShadow: `0 0 5px ${color1}, 0 0 20px ${color2}`,
          };
        }
      }
      // this adds the utility classes to Tailwind
      addUtilities(neonUtilities);
    }),
  ],
};
```

Then we can use our newly created utility classes straight in our HTML (or JSX):

```html
<div class="m-20 w-20 h-10 rounded-lg neon-blue"></div>
```

Notice that we can change to any color in the Tailwind palette we want:

<video muted autoplay loop playsinline controls>
      <source src="https://cdn.builder.io/o/assets%2FYJIGb4i01jvw0SRdL5Bt%2F185d44d8f0a045678bbd460150ca313c%2Fcompressed?apiKey=YJIGb4i01jvw0SRdL5Bt&amp;token=185d44d8f0a045678bbd460150ca313c&amp;alt=media&amp;optimized=true" type="video/mp4">
    </source></video>
## Importing Tailwind colors as an object

We can import Tailwind colors as an object inside JavaScript. This can be extremely useful if we want to use the Tailwind colors to create our own custom theme, or add another name to the color palette.

For example, we can create a `primary` color that will be added as a class:

```js
// import all colors from Tailwind
const colors = require('tailwindcss/colors');

module.exports = {
  content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
  theme: {
    extend: {
      colors: {
        // set "primary" class name to desired color + set default.
        primary: { ...colors.sky, DEFAULT: colors.sky['600'] },
      },
    },
  },
  plugins: [],
};
```

## Using variants

A common use case when building components is having some sort of base or default style that can be overridden, by passing a class or a prop.

The `tailwind-merge` package is very useful to deal with this. It allows us to pass the base classes as the first parameter and the class name as the second parameter, ensuring that our class name overrides the default class (for a deeper dive as to how this works, see [this video](https://www.youtube.com/watch?v=tfgLd5ZSNPc)).

Here’s an example:

```ts
import { twMerge } from 'tailwind-merge'

const SpecialButton: React.FC<{ className?: string }> = ({ className }) => {
  return (
    <button className={
      twMerge('px-2 py-1 bg-red-500 hover:bg-red-800', className)}
    >
      Click me!
    </button>
  )
}

// Then we can override the style like so:

// some-other-component.js

const Component = () => {
  <div>
    <h1>Hello!</h1>
    <SpecialButton className="bg-blue-500 hover:bg-blue-800" />
  </div>
}

```

### Bonus: with Class Variant Authority

[CVA](https://cva.style/docs) is a package to help you create variants in a more elegant way. To make it work with `tailwind-merge` we can do the following:

```ts
import { cva, type VariantProps } from "class-variance-authority";
import { twMerge } from "tailwind-merge";
 
const buttonVariants = cva(["your", "base", "classes"], {
  variants: {
    intent: {
      primary: ["your", "primary", "classes"],
    },
  },
  defaultVariants: {
    intent: "primary",
  },
});
 
export interface ButtonVariants extends VariantProps<typeof buttonVariants> {};
 
export const buttonClasses = (variants: ButtonVariants) =>
  twMerge(buttonVariants(variants));
```

## Easy gradients

You can create complex gradients using [gradient color stops](https://tailwindcss.com/docs/gradient-color-stops#middle-color). To do so we can use the `bg-gradient-to-` class and combine it with `t` (top), `r` (right), `b` (bottom), and `l` (left). We can also state corners with `tr` (top-right), `bl` (bottom-left), etc.

And then we can combine: `from`, `to`, and `via` to make some stunning gradients.

Let’s have a look at some examples:

```ts
{ /* the first "to" 👇🏽 is specifiying the direction */}
<div class="bg-gradient-to-r from-indigo-500 ...">
{ /* the "from-" sets which color to start at and then fades out */}
```

The rendered output would be a gradient that starts with indigo and fades to transparent:

 ![Untitled](https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2Fc7a923f46af04f66bf9ba4179472bbd1?width=300)

To set the ending we can use the `to-`:

```html
<div class="bg-gradient-to-r from-indigo-500 to-pink-400...">
```

That would render a gradient that starts with indigo and fades to pink:

 ![Untitled](https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2Fea83c9602f7847eaaca8e8391bd0519d?width=300)

To add pizzaz we can control which color is in the middle by using `via` between:

```html
<div class="bg-gradient-to-r from-indigo-500 via-green-400 to-pink-400...">
```

That would render an almost rainbow gradient, as such:

 ![Untitled](https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F665e22e445df440ba6c0341491a38835?width=300)
## Truncate your text easily

Another nifty utility class is `line-clamp`, which allows you to truncate multiline text by simply adding a number such as `line-clamp-3`:

```html
<article class="mt-20 border border-slate-300 rounded-md p-4 ml-6 text-white w-60">
  <p class="line-clamp-3">
    Nulla dolor velit adipisicing duis excepteur esse in duis nostrud
    occaecat mollit incididunt deserunt sunt. Ut ut sunt laborum ex
    occaecat eu tempor labore enim adipisicing minim ad. Est in quis eu
    dolore occaecat excepteur fugiat dolore nisi aliqua fugiat enim ut
    cillum. Labore enim duis nostrud eu. Est ut eiusmod consequat irure
    quis deserunt ex. Enim laboris dolor magna pariatur. Dolor et ad sint
    voluptate sunt elit mollit officia ad enim sit consectetur enim.
  </p>
</article>
```

The rendered result will put an ellipsis after 3 lines of text:

 ![Untitled](https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F67e62443191443c1b62a85e49f51302f?width=450)

## Styling the un-styleable
Styling things like `<input type="checkbox">` has been notoriously hard. No more, with the `accent-{color}` modifier:
```html
<label>
  <input type="checkbox" checked> Browser default
</label>
<label>
  <input type="checkbox" class="accent-pink-500" checked> Customized
</label>
```

## Container queries
One of the [new CSS features](https://www.builder.io/blog/css-the-good-parts#b-a-peek-into-the-near-future-b) that a lot of folks are rightfully excited about. They allow applying styles based on the size of the element itself. There's a plugin to start using them now in Tailwind called [`@tailwindcss/container-queries`](https://github.com/tailwindlabs/tailwindcss-container-queries).

After adding this plugin to your project, an element can be mark with `@container` and children can use variants like `@sm` and `@md`:

```html
<div class="@container">
  <div class="@lg:text-sky-400">
    <!-- ... -->
  </div>
</div>
```

## Conclusion

And that's it! These are just some of the many tips and tricks available to you when using Tailwind CSS. With its wide range of utility classes and responsive design capabilities, the possibilities are endless. So get creative, have fun, and don't be afraid to experiment with new styles and designs. With Tailwind CSS, you can create beautiful and functional websites with ease. Happy coding! 🚀

 ![](https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F83677fb912dd4a539a3f3bb5c0c82de1?width=405)<video muted autoplay loop playsinline controls>
      <source src="https://cdn.builder.io/o/assets%2FYJIGb4i01jvw0SRdL5Bt%2F77cc5249f71948568195e7314bcc5766%2Fcompressed?apiKey=YJIGb4i01jvw0SRdL5Bt&amp;token=77cc5249f71948568195e7314bcc5766&amp;alt=media&amp;optimized=true" type="video/mp4">
    </source></video>

Visually build with your components

[Builder.io](https://www.builder.io/m/developers) is a headless CMS that lets you [drag and drop](https://www.builder.io/m/products) with [**your** components](https://www.builder.io/m/developers) right within your existing site.

[Try it out](https://builder.io/signup) [Learn more](/m/developers) 
```ts
// Dynamically render your components
export function MyPage({ json }) {
  return <BuilderComponent content={json} />
}

registerComponents([MyHero, MyProducts])
```

##### _Read the [full post](https://www.builder.io/blog/tailwind-css-tips-and-tricks) on the [Builder.io blog](https://www.builder.io/blog)_