Since 2023 Tailwind CSS has become the de-facto utility-first framework on the frontend. You can build production-ready UI just by adding class names, no CSS file needed. This guide walks through every fundamental, from installation to custom theming.

Installation

# Next.js / React / Vite project
npm install -D tailwindcss@latest postcss autoprefixer
npx tailwindcss init -p

# Standalone (no build tool)
npm install -D @tailwindcss/cli
npx @tailwindcss/cli -i input.css -o output.css --watch

# CDN (for prototypes)
<script src="https://cdn.tailwindcss.com"></script>
/* src/styles.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

Utility-First Philosophy

Tailwind's core idea: keep class names short and compose UIs inline. Instead of padding: 1rem write p-4; instead of background-color: #3b82f6 write bg-blue-500.

<!-- Classic CSS
<style>
    .card { padding: 1rem; background: white;
            border-radius: 0.5rem; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
    .card-title { font-size: 1.25rem; font-weight: 600; margin-bottom: 0.5rem; }
</style>
<div class="card">
    <h2 class="card-title">Title</h2>
    <p>Content</p>
</div>
-->

<!-- Tailwind -->
<div class="p-4 bg-white rounded-lg shadow">
    <h2 class="text-xl font-semibold mb-2">Title</h2>
    <p>Content</p>
</div>

Responsive Design

Tailwind's breakpoint prefixes are mobile-first: no prefix is the default (mobile), then sm: (640px+), md: (768px+), lg: (1024px+), xl: (1280px+), 2xl: (1536px+).

<!-- Single column on mobile, 2 on tablet, 3 on desktop -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
    <div class="p-4 bg-gray-100">Card 1</div>
    <div class="p-4 bg-gray-100">Card 2</div>
    <div class="p-4 bg-gray-100">Card 3</div>
</div>

<!-- Small heading on mobile, larger on desktop -->
<h1 class="text-2xl md:text-4xl lg:text-5xl font-bold">Big Heading</h1>

Dark Mode

// tailwind.config.js
module.exports = {
    darkMode: 'class',  // or 'media'
    content: ['./src/**/*.{html,js,jsx,tsx}']
};
<html class="dark">
<body class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
    <div class="p-4 bg-gray-50 dark:bg-gray-800 rounded">
        Automatic dark mode
    </div>
</body>
// Dark mode toggle
const toggle = document.getElementById('themeToggle');
toggle.addEventListener('click', () => {
    document.documentElement.classList.toggle('dark');
    localStorage.setItem('theme',
        document.documentElement.classList.contains('dark') ? 'dark' : 'light');
});
// On page load
if (localStorage.theme === 'dark') {
    document.documentElement.classList.add('dark');
}

Custom Theme

// tailwind.config.js
module.exports = {
    content: ['./src/**/*.{html,js,jsx,tsx}'],
    theme: {
        extend: {
            colors: {
                brand: {
                    50:  '#fef3ee',
                    500: '#ea580c',   // keydal orange
                    700: '#9a3412'
                }
            },
            fontFamily: {
                serif: ['Instrument Serif', 'Georgia', 'serif'],
                sans:  ['DM Sans', 'system-ui', 'sans-serif']
            },
            animation: {
                'fade-in': 'fadeIn 0.3s ease-out'
            },
            keyframes: {
                fadeIn: {
                    '0%':   { opacity: 0, transform: 'translateY(10px)' },
                    '100%': { opacity: 1, transform: 'translateY(0)' }
                }
            }
        }
    }
};
<button class="bg-brand-500 hover:bg-brand-700 text-white px-4 py-2 rounded font-sans">
    Button using brand colors
</button>
<div class="animate-fade-in">Fade-in animation</div>

Component Extraction

Repeating the same class list? Extract it into a component — in HTML/JSX, not in CSS.

// React component
function Button({ children, variant = 'primary' }) {
    const variants = {
        primary: 'bg-blue-500 hover:bg-blue-600 text-white',
        secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-900',
        danger: 'bg-red-500 hover:bg-red-600 text-white'
    };
    return (
        <button className={`px-4 py-2 rounded font-medium ${variants[variant]}`}>
            {children}
        </button>
    );
}

// Usage
<Button variant="primary">Save</Button>
<Button variant="danger">Delete</Button>

CSS Abstraction with @apply

Sometimes it is useful to keep class names in CSS. The @apply directive inlines utilities into CSS rules — handy when you are not using a component framework.

@layer components {
    .btn {
        @apply px-4 py-2 rounded font-medium transition-colors;
    }
    .btn-primary {
        @apply btn bg-blue-500 hover:bg-blue-600 text-white;
    }
}

Arbitrary Values

<!-- Custom color, size, etc. -->
<div class="bg-[#ea580c] w-[calc(100%-2rem)] text-[15px]">
    Values not in the config
</div>
<div class="grid grid-cols-[200px_1fr_100px] gap-[14px]">
    Custom grid template
</div>

Production Build

Tailwind ships only the classes you actually use in the final CSS (built-in PurgeCSS-like behaviour). A full 3MB Tailwind file typically shrinks to 5-30KB in production.

# Build
NODE_ENV=production npx tailwindcss -i input.css -o output.css --minify

Conclusion

After a two-hour learning curve, Tailwind CSS becomes the fastest tool for writing UI. You skip CSS files, you skip naming, and you get responsive design and dark mode for free — an ideal foundation for production-quality interfaces.

Frontend development with Tailwind

Tailwind CSS setup and design-system work inside your React/Vue project Write to us

WhatsApp