f in x
> cd .. / HUB_EDITORIALE
Design, Web & Comunicazione

Tailwind CSS for Modern UI — Setup, Components and Optimization

[2026-06-23] Author: Ing. Calogero Bono

How many times have you started a custom CSS project and ended up with 3000 lines of styles, forgotten utility classes, and a designer asking for 'just one more color variant'? The problem is maintenance. At Meteora Web, we experienced this limit on dozens of sites and apps. Then we adopted Tailwind CSS on a Laravel + Vue project for a client in southern Italy. The result: CSS bundle from 400KB to 23KB, development 3x faster, a consistent design system without writing a single line of custom CSS. Since then, Tailwind has become our main UI tool. In this pillar page we explain why, how to set it up from basics to advanced patterns, and how to avoid the common mistakes we see every day. All with our voice: technical, practical, and ROI-focused.

How to Set Up Tailwind CSS 4 and What Has Changed from Tailwind 3

If you come from previous versions, the first difference is the simplified configuration file. Tailwind 4 introduces a zero-config approach for most cases. Just install the package and import the stylesheet into your Vite or Next.js project.

Installation with npm and Vite

npm install tailwindcss @tailwindcss/vite

Then add the plugin in vite.config.js:

import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [tailwindcss()]
})

And in your main CSS:

@import "tailwindcss";

That's it. No more huge tailwind.config.js: most customizations happen in CSS via @theme. For example:

@theme {
  --color-primary: #0055ff;
  --font-family-sans: 'Inter', sans-serif;
}

Key new features in Tailwind 4: native container query support, OKLCH colors, automatic dark/light variants, and smoother animation handling. For official docs, visit tailwindcss.com.

Sponsored Protocol

How to Handle Responsive Design with Tailwind

Mobile-first is the mantra. Classes without prefix apply to all screens; prefixes like sm:, md:, lg: activate from that breakpoint upward. Default breakpoints: sm=640px, md=768px, lg=1024px, xl=1280px, 2xl=1536px.

Fluid Layouts with Grid and Flex

A pattern we use daily:

Card 1
Card 2
Card 3

No custom media queries, no extra classes. The design system lives in the markup. Practical tip: don't force fixed breakpoints. Let the layout collapse naturally. If a component breaks at 1023px, review the classes, don't add another breakpoint.

Dark Mode with Tailwind: How to Implement It Without Pain

You can choose between two strategies: media (based on OS preference) or class (controlled via JavaScript). We almost always recommend class because it gives user control and allows manual toggle.

Configuration in Tailwind 4

Add this to your CSS:

@import "tailwindcss";
@variant dark (&:where(.dark, .dark *));

Now just use dark: prefix:

Content that adapts to theme

For the toggle button in JavaScript:

document.documentElement.classList.toggle('dark');

Warning: don't forget to persist state (localStorage) and avoid initial style flash. We solve it with an inline script in <head> that reads the saved theme before CSS renders.

Reusable Tailwind CSS Components: @apply or Pure Components?

This is a common question. The answer: it depends, but we prefer pure components. Here's why.

The @apply Problem

@apply lets you aggregate utilities into a single CSS class. Seems handy: create .btn-primary with @apply bg-blue-500 px-4 py-2 rounded. But over time it leads to two issues:

Sponsored Protocol

  • Duplication: when a designer asks for a variant, you must modify both the class and all its usages.
  • Loss of the utility-first pattern: the markup no longer shows design choices, and your CSS bundle grows.

We use @apply only for truly common utilities like .text-body or .container-wide that have no variants. For every component, we create a React / Vue / Blade component that contains the utility classes directly.

Example of a Reusable Component in React

function Button({ variant = 'primary', children }) {
  const base = 'px-4 py-2 rounded font-semibold transition'
  const variants = {
    primary: 'bg-blue-500 text-white hover:bg-blue-600',
    secondary: 'bg-gray-200 text-black hover:bg-gray-300'
  }
  return (
    
  )
}

No @apply, no extra CSS, maximum flexibility.

Tailwind CSS with React: How to Handle Dynamic className and cva

As components grow, managing classes statically becomes complex. Two libraries solve the problem: clsx and twMerge. We use them together.

clsx for Conditions

import clsx from 'clsx'

twMerge for Merging and Overriding

tailwind-merge resolves conflicting classes: if you pass px-4 then px-6, twMerge keeps the last one. Perfect for components with exposed className props.

import { twMerge } from 'tailwind-merge'

function Card({ className, children }) {
  return (
    
{children}
) }

cva (Class Variance Authority)

For complex variants, class-variance-authority is the standard solution. Define variants, compound variants, and defaults.

import { cva } from 'class-variance-authority'

const button = cva('px-4 py-2 rounded', {
  variants: {
    intent: { primary: 'bg-blue-500', secondary: 'bg-gray-200' },
    size: { sm: 'text-sm', md: 'text-base', lg: 'text-lg' }
  },
  defaultVariants: { intent: 'primary', size: 'md' }
})

function Button({ intent, size, children }) {
  return 
}

Classes remain atomic, logic is centralized.

Sponsored Protocol

Tailwind CSS with Laravel and Blade: Configuration and Optimization

In our stack, Laravel + Tailwind has been a great match for years. Configuration with Vite is standard, but with a few production-specific tweaks.

Content Paths in Vite

Make sure Vite knows where to look for used classes in Blade files:

// vite.config.js
export default defineConfig({
  // ...
  content: [
    './resources/**/*.blade.php',
    './vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php'
  ]
})

Without this, Tailwind includes all framework classes, bloating the bundle. We've seen a project go from 2MB to 80KB just by adding this line.

Production Optimization

In production, Vite runs PurgeCSS automatically if you set content. Also enable minification and gzip/Brotli compression at the server level. Our standard setup includes @tailwindcss/optimize (Tailwind 4) to remove duplicate classes.

Shadcn/UI and Radix UI: Accessible Components with Tailwind

For enterprise projects requiring complex components (dropdowns, dialogs, tabs) with WCAG accessibility, shadcn/ui (built on Radix UI) is the best choice. It's not a library in the classic sense: you download the component code into your project and customize it. Every component uses Tailwind for styling.

Why We Use It

Radix handles accessibility (focus management, role, aria) flawlessly. Shadcn provides ready-made templates with Tailwind. We've integrated it into Laravel + Inertia and Vue + Nuxt projects. Result: 40% faster development and zero accessibility bugs.

Sponsored Protocol

Installation example:

npx shadcn@latest add button

The component is created in components/ui/button.tsx and you can modify it with your own Tailwind variants.

Animations with Tailwind: Transition, Animate, and Custom Animations

Transition: use transition + duration-300 + ease-in-out for hover, focus, or state changes. Animate: built-in classes like animate-spin, animate-ping, animate-pulse for ready-made effects.

Custom Animations in Tailwind 4

Define animations directly in CSS with @keyframes then expose them as utilities:

@keyframes fade-up {
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: translateY(0); }
}
@theme {
  --animate-fade-up: fade-up 0.6s ease-out;
}

Use it in markup: <div class="animate-fade-up">. No custom classes, no extra CSS.

Complex Layouts with Tailwind: Grid, Flexbox, and Responsive Patterns

We've built dashboards, e-commerce sites, and landing pages with complex layouts. Two patterns we use constantly:

Auto-fill with Grid

...

Flexbox for Navbar


The rule: always start with flex or grid, then add breakpoints. Never write desktop-first layouts — mobile-first avoids unnecessary overrides.

Optimizing Tailwind Build: How to Reduce Bundle Size

Tailwind's advantage is a small build. But if you don't configure content paths correctly, you end up with hundreds of KB of unused classes. Here's how to maximize optimization.

Automatic PurgeCSS

In Tailwind 4, purging is integrated into Vite, but you must specify content (or @source in CSS). Example:

Sponsored Protocol

@import "tailwindcss";
@source "../views/";
@source "../components/";

For projects with many templates, use @source to point to folders. We recommend adding a safelist for classes used dynamically (e.g., from API).

Real Result

One of our e-commerce clients (Shopify + custom Tailwind) had a 1.2MB CSS build. After configuring content paths and safelist, the file dropped to 45KB. That's a 96% reduction and a PageSpeed score jump from 45 to 92. Not a marginal improvement: it's the difference between losing customers and converting them.

What To Do Now

Here are 5 concrete actions you can take right now:

  1. Install Tailwind 4 in a test project (Vite + vanilla HTML) and build a responsive card with dark mode.
  2. Read the official documentation at tailwindcss.com to get familiar with core utilities.
  3. Integrate clsx and twMerge in your React or Vue project: you'll reduce conflicts and boilerplate.
  4. Check your current site's CSS build: if it exceeds 100KB, you probably have unused classes. Add content paths and measure the improvement.
  5. Explore shadcn/ui for accessible components: install a button and a dialog, and see how fast you get a professional UI.

And if you manage a Laravel or WordPress project, check out our articles on Advanced WordPress Development and Custom Gutenberg Blocks with React — both use Tailwind as their UI foundation.

We at Meteora Web chose Tailwind not because it's trendy, but because it saved us hours of work and improved project quality. If you have doubts or need help optimizing your UI, write us. We talk numbers, not theory.

Ing. Calogero Bono

> AUTHOR_EXTRACTED

Ing. Calogero Bono

Ingegnere Informatico, co-fondatore di Meteora Web. Esperto in architetture software, sicurezza informatica e sviluppo sistemi scalabili.
[ Read Full Dossier ]

> METEORA_WEB // DIGITAL AGENCY

We build the digital presence your business deserves.

Websites, social media, online advertising, e-commerce and high-performance hosting, engineered with method by computer engineers in Sciacca, for all of Italy.

> MW_JOURNAL

> READ_ALL()