November 3, 2024
10 tips with TailwindCSS

Over the past couple of years, TailwindCSS has become one of the most popular CSS frameworks for building modern websites. The beauty of TailwindCSS is that it allows you to build complex designs without worrying about creating separate components. This post will show you 10 tips that will help you get the most out of TailwindCSS.

10 tips with TailwindCSS

Introduction#

TailwindCSS is a utility-first CSS framework that allows you to build complex designs from a composition of small, single-purpose utility classes directly in your markup. TailwindCSS works perfectly with browser-heavy frameworks like React, Vue and server-side frameworks like Next.js thanks to its efficiency and zero runtime.

TailwindCSS proves that it resolves many issues of working with CSS and UI systems. You can have a better grasp of your components by looking at them in your components’ markup. You save time and energy from coming up with names for a simple component.

Download Comparison

Arbitrary values#

TailwindCSS ships with a predefined set of utility classes that you can use immediately. For example, Tailwind provides a set of predefined color system and spacing system. You can use them directly in your markup.

<div className="bg-blue-500 p-4"> <p className="text-white">Hello, world!</p> </div>

However, you can also use arbitrary values in your utility classes with the syntax property-[value]. For example:

<div class="bg-[#bada55] text-[22px] before:content-['Festivus'] grid grid-cols-[fit-content(theme(spacing.32))]" > <!-- ... --> </div>

This feature works very well with properties that support arbitrary values like background-color, font-size, color, etc. However, properties like display don’t support arbitrary values.

Arbitrary properties#

TailwindCSS also allows you to use arbitrary properties in your utility classes to use some CSS properties that are not covered by TailwindCSS. For example:

<div class="[mask-type:luminance] hover:[mask-type:alpha] [display:block]"> <!-- ... --> </div>

This syntax is like how you would write CSS in a stylesheet and you wrap around the property with square brackets. You can also use this feature to write custom CSS variables:

<div class="[--my-custom-property:10px]"> <!-- ... --> </div>

Dynamic styles#

A common case in building web components is to apply styles based on some sort of states. For example, we have a button component that changes its background color based on the state of the button.

{/* This won't work */} <button className={`bg-${ state === "danger" ? "red" : ( state === "warning" ? "yellow" : "blue" )}-500`}> </button>

This won’t work because TailwindCSS doesn’t support dynamic styles or any client-side logics. Every class names must be extractable at build time.

{/* This will work */} <button className={`${ state === "danger" ? "bg-red-500" : ( state === "warning" ? "bg-yellow-500" : "bg-blue-500" )}`}> </button>

Client-side styles#

But what if there is a situation where you really need to apply styles from the client-side? For example, you are building a floating modal that needs the current width of the device screen so that you can set the appropriate padding

import { useWindowSize } from "@uidotdev/usehooks"; function App() { const { width } = useWindowSize(); return ( <main className="flex flex-col items-center gap-8 py-16 max-w-[1280px] mx-auto" style={ { "--container-size": `${width}px`, } as CSSProperties } > {/* ... */} <p className="absolute bottom-4 bg-amber-500 p-4 rounded-lg w-[calc(var(--container-size)-48px)] left-[24px] border border-yellow-700 text-amber-50"> Click on the Vite and React logos to learn more </p> </main> ); }

Traditional classnames#

Having a lot of utility classes in your markup can be overwhelming. Imaging looking at a component like this:

<div class="rounded-s w-full h-full [&_img]:scale-100 group-hover:[&_img]:scale-105 group-hover:[&_video]:scale-105 [&_img]:transform-gpu [&_video]:transform-gpu [&_img]:transition-transform [&_img]:ease-curve-d [&_img]:duration-normal [&_video]:transition-transform [&_video]:ease-curve-d [&_video]:duration-normal w-1/2 mx-auto transition-opacity ease-curve-c duration-normal max-w-media relative"> <!-- ... --> </div>

Sometimes with components like this, it’s better to put all the CSS rules in a traditional CSS class and use that one class in your markup. Luckily, TailwindCSS provides a native way to do this with the @apply directive.

.card { @apply rounded-s w-full h-full mx-auto relative max-w-media; @apply [&_img]:scale-100 group-hover:[&_img]:scale-105 group-hover:[&_video]:scale-105; @apply [&_img]:transform-gpu [&_img]:transition-transform [&_img]:ease-curve-d [&_img]:duration-normal @apply [&_video]:transform-gpu [&_video]:transition-transform [&_video]:ease-curve-d [&_video]:duration-normal; @apply transition-opacity ease-curve-c duration-normal; }
<div class="card"> <!-- ... --> </div>

This way is very useful particularly when you write blogs or documentation that uses many uniformed, reusable components.

Directives#

However, writing custom CSS classes in CSS files does not give a good developing experience. The first thing is that it might cause some compiling issues if you are not careful with the @layers directive in TailwindCSS1. Secondly, intellisense on your editor, like Tailwind CSS IntelliSense does not give any suggestions for your custom CSS classes.

To solve this issue, you can use the Tailwind Plugin System to create reusable, custom styles. For example:

import plugin from "tailwindcss/plugin"; const config = { darkMode: ["class"], content: [ "./pages/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}", "./src/**/*.{ts,tsx,mdx,md,html}", ], prefix: "", corePlugins: {}, theme: {}, plugins: [ // Other plugins plugin(function ({ addUtilities, theme }) { addUtilities({ ".content-container": { marginLeft: "var(--article-gutter-size)", marginRight: "var(--article-gutter-size)", marginBottom: theme("spacing.4"), width: "var(--article-container-size)", }, }); }), plugin(function ({ addComponents, theme }) { addComponents({}); }), ], }; export default config;

Modern text editor with TailwindCSS Intellisense will tell you what the class is expanded to.

Custom Tailwind Directive

Using TailwindCSS Intellisense#

Speaking of TailwindCSS Intellisense, it is a must have extension for developers to work with TailwindCSS. It provides

  • Autocomplete. Intelligent suggestions for class names, as well as CSS functions and directives.
  • Linting. Highlights errors and potential bugs in both your CSS and your markup.
  • Hover Previews. See the complete CSS for a Tailwind class name by hovering over it.
  • Syntax Highlighting. Provides syntax definitions so that Tailwind features are highlighted correctly.

Tailwind CSS IntelliSense

Unfortunately, TailwindCSS Intellisense does not provide any official support for other editors than Visual Studio Code. However, TailwindCSS Intellisense also provides a Language Server Protocol (LSP) that you can use to build your own TailwindCSS Intellisense for other editors.

Using TailwindCSS Prettier Plugin#

Another must-have extension for working with TailwindCSS in VsCode is the prettier-plugin-tailwindcss. This plugin simply sorts and organizes your utility classes in your markup based on the TailwindCSS recommended order.

TailwindCSS Prettier Plugin

Since this is a Prettier plugin, it will work seamlessly wherever Prettier works in your project, popular IDEs or text editors, and even on command line.

<!-- Before --> <button class="text-white px-4 sm:px-8 py-2 sm:py-3 bg-sky-700 hover:bg-sky-800">...</button> <!-- After --> <button class="bg-sky-700 px-4 py-2 text-white hover:bg-sky-800 sm:px-8 sm:py-3">...</button>

Override TailwindCSS classes#

Overriding existing styles in existing components is a common case when working with TailwindCSS. An easy and direct way to override classes is to use the important modifier !. For example:

<div class="bg-blue-500 !bg-red-500"> <!-- ... --> </div>

However, thou shalt not use !important in CSS. Another way to override is to use tailwind-merge.

import { twMerge } from 'tailwind-merge' twMerge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]') // → 'hover:bg-dark-red p-3 bg-[#B91C1C]'

Refactor TailwindCSS classes#

As mentioned above, having a lot of utility classes in your markup is a pain. Put all the CSS rules in a traditional CSS class is helpful. But what if the component contains some conditions such as states, variants, etc.? Or even, you just want to break down the utility classes into smaller pieces that fit your 80-character line limit?

Packages like classnames and clsx allow you to combine multiple classes into one classname string. For example:

import * as React from "react"; import clsx from "clsx"; interface ButtonProps { size: "sm" | "md" | "lg"; color: "red" | "green" | "blue"; } export const Button: React.FC<ButtonProps> = ({ variant, size, color }) => { return ( <button className={clsx( "px-4 py-2", size === "sm" && "text-sm", size === "md" && "text-md", size === "lg" && "text-lg", color === "red" && "bg-red-500", color === "green" && "bg-green-500", color === "blue" && "bg-blue-500" )} > Click me </button> ); };

You can also combine clsx with tailwind-merge to create a robust utility function for managing your styles. For example, creating a reusable function to handle that all together:

import { type ClassValue, clsx } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }

And then use it in your reusable components that allow you to specify the class later:

import * as React from "react"; import { cn } from "@/lib/utils"; const Card = React.forwardRef< HTMLDivElement, React.HTMLAttributes<HTMLDivElement> >(({ className, ...props }, ref) => ( <div ref={ref} className={cn( "rounded-xl border bg-card text-card-foreground shadow", className, )} {...props} /> )); Card.displayName = "Card";

Conclusion#

TailwindCSS is a powerful tool that allows you to build complex designs with ease. By following these tips, you can get the most out of TailwindCSS and create beautiful, responsive websites quickly and efficiently. Remember that like any other tools, TailwindCSS has its own strengths and weaknesses. I hope this post has given you some insights into how to get the most out of TailwindCSS.

References#

Footnotes#

  1. Using @apply with per-component CSS

Tags: