Introduction to Tailwind CSS

Tailwind CSS is a utility-first CSS framework that provides low-level utility classes to build custom designs without writing custom CSS. Instead of predefined components, you compose designs using utility classes directly in your HTML.

Installation

# Install Tailwind CSS
npm install -D tailwindcss postcss autoprefixer

# Generate config files
npx tailwindcss init -p

# tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

# Add to your CSS file (src/index.css)
@tailwind base;
@tailwind components;
@tailwind utilities;

Core Concepts

Utility Classes

<!-- Traditional CSS approach -->
<div class="card">
  <h2 class="card-title">Title</h2>
  <p class="card-text">Content</p>
</div>

<!-- Tailwind utility-first approach -->
<div class="bg-white rounded-lg shadow-md p-6">
  <h2 class="text-xl font-bold mb-2">Title</h2>
  <p class="text-gray-600">Content</p>
</div>

Spacing (Margin & Padding)

<!-- Padding -->
<div class="p-4">All sides: 1rem</div>
<div class="px-4">Horizontal: 1rem</div>
<div class="py-2">Vertical: 0.5rem</div>
<div class="pt-6 pb-4">Top: 1.5rem, Bottom: 1rem</div>
<div class="pl-8 pr-2">Left: 2rem, Right: 0.5rem</div>

<!-- Margin -->
<div class="m-4">All sides: 1rem</div>
<div class="mx-auto">Center horizontally</div>
<div class="mt-8 mb-4">Top: 2rem, Bottom: 1rem</div>
<div class="-mt-4">Negative margin: -1rem</div>

<!-- Space between children -->
<div class="space-y-4">
  <div>Item 1</div>
  <div>Item 2</div>  <!-- Has margin-top: 1rem -->
  <div>Item 3</div>  <!-- Has margin-top: 1rem -->
</div>

<!-- Spacing scale: 0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 64, 72, 80, 96 -->
<!-- 1 unit = 0.25rem = 4px (default) -->

Colors

<!-- Text colors -->
<p class="text-black">Black text</p>
<p class="text-white">White text</p>
<p class="text-gray-500">Gray 500</p>
<p class="text-blue-600">Blue 600</p>
<p class="text-red-500">Red 500</p>

<!-- Background colors -->
<div class="bg-white">White background</div>
<div class="bg-gray-100">Light gray</div>
<div class="bg-blue-500">Blue background</div>
<div class="bg-gradient-to-r from-blue-500 to-purple-500">Gradient</div>

<!-- Border colors -->
<div class="border border-gray-300">Gray border</div>
<div class="border-2 border-blue-500">Blue border</div>

<!-- Opacity -->
<div class="bg-black/50">50% opacity black</div>
<div class="text-blue-500/75">75% opacity blue text</div>

<!-- Color scales: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950 -->

Typography

<!-- Font size -->
<p class="text-xs">Extra small (0.75rem)</p>
<p class="text-sm">Small (0.875rem)</p>
<p class="text-base">Base (1rem)</p>
<p class="text-lg">Large (1.125rem)</p>
<p class="text-xl">Extra large (1.25rem)</p>
<p class="text-2xl">2XL (1.5rem)</p>
<p class="text-4xl">4XL (2.25rem)</p>

<!-- Font weight -->
<p class="font-light">Light (300)</p>
<p class="font-normal">Normal (400)</p>
<p class="font-medium">Medium (500)</p>
<p class="font-semibold">Semibold (600)</p>
<p class="font-bold">Bold (700)</p>

<!-- Line height -->
<p class="leading-none">1</p>
<p class="leading-tight">1.25</p>
<p class="leading-normal">1.5</p>
<p class="leading-relaxed">1.625</p>
<p class="leading-loose">2</p>

<!-- Text alignment -->
<p class="text-left">Left</p>
<p class="text-center">Center</p>
<p class="text-right">Right</p>
<p class="text-justify">Justify</p>

<!-- Text decoration -->
<p class="underline">Underlined</p>
<p class="line-through">Strikethrough</p>
<p class="no-underline">No underline</p>

<!-- Text transform -->
<p class="uppercase">uppercase</p>
<p class="lowercase">LOWERCASE</p>
<p class="capitalize">capitalize words</p>

Layout with Flexbox & Grid

Flexbox

<!-- Basic flex container -->
<div class="flex">
  <div>Item 1</div>
  <div>Item 2</div>
  <div>Item 3</div>
</div>

<!-- Direction -->
<div class="flex flex-row">...</div>        <!-- Horizontal (default) -->
<div class="flex flex-col">...</div>        <!-- Vertical -->
<div class="flex flex-row-reverse">...</div> <!-- Reversed -->

<!-- Justify content (main axis) -->
<div class="flex justify-start">...</div>    <!-- Start -->
<div class="flex justify-center">...</div>   <!-- Center -->
<div class="flex justify-end">...</div>      <!-- End -->
<div class="flex justify-between">...</div>  <!-- Space between -->
<div class="flex justify-around">...</div>   <!-- Space around -->
<div class="flex justify-evenly">...</div>   <!-- Space evenly -->

<!-- Align items (cross axis) -->
<div class="flex items-start">...</div>      <!-- Start -->
<div class="flex items-center">...</div>     <!-- Center -->
<div class="flex items-end">...</div>        <!-- End -->
<div class="flex items-stretch">...</div>    <!-- Stretch -->
<div class="flex items-baseline">...</div>   <!-- Baseline -->

<!-- Center both axes -->
<div class="flex items-center justify-center h-screen">
  <p>Perfectly centered</p>
</div>

<!-- Flex wrap -->
<div class="flex flex-wrap">...</div>
<div class="flex flex-nowrap">...</div>

<!-- Flex grow/shrink -->
<div class="flex">
  <div class="flex-none w-20">Fixed width</div>
  <div class="flex-1">Takes remaining space</div>
  <div class="flex-none w-20">Fixed width</div>
</div>

<!-- Gap -->
<div class="flex gap-4">...</div>       <!-- Gap all sides -->
<div class="flex gap-x-4 gap-y-2">...</div> <!-- Different gaps -->

CSS Grid

<!-- Basic grid -->
<div class="grid grid-cols-3 gap-4">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
</div>

<!-- Column count -->
<div class="grid grid-cols-1">...</div>   <!-- 1 column -->
<div class="grid grid-cols-2">...</div>   <!-- 2 columns -->
<div class="grid grid-cols-4">...</div>   <!-- 4 columns -->
<div class="grid grid-cols-12">...</div>  <!-- 12 columns -->

<!-- Auto columns -->
<div class="grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))]">
  <!-- Responsive grid that fits as many 250px+ columns as possible -->
</div>

<!-- Column span -->
<div class="grid grid-cols-4 gap-4">
  <div class="col-span-1">1 column</div>
  <div class="col-span-2">2 columns</div>
  <div class="col-span-1">1 column</div>
  <div class="col-span-4">Full width</div>
</div>

<!-- Rows -->
<div class="grid grid-rows-3 grid-flow-col">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
</div>

<!-- Row span -->
<div class="grid grid-cols-3 grid-rows-3 gap-4">
  <div class="row-span-2">Spans 2 rows</div>
  <div>Normal</div>
  <div class="col-span-2 row-span-2">2x2 area</div>
</div>

Responsive Design

Breakpoints

<!-- Tailwind breakpoints (mobile-first) -->
<!-- sm: 640px, md: 768px, lg: 1024px, xl: 1280px, 2xl: 1536px -->

<!-- Responsive classes -->
<div class="w-full md:w-1/2 lg:w-1/3">
  <!-- Full width on mobile, half on tablet, third on desktop -->
</div>

<!-- Responsive layout -->
<div class="flex flex-col md:flex-row gap-4">
  <div class="md:w-1/3">Sidebar</div>
  <div class="md:w-2/3">Content</div>
</div>

<!-- Responsive grid -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
  <div>Card 1</div>
  <div>Card 2</div>
  <div>Card 3</div>
  <div>Card 4</div>
</div>

<!-- Responsive typography -->
<h1 class="text-2xl sm:text-3xl md:text-4xl lg:text-5xl">
  Responsive Heading
</h1>

<!-- Responsive spacing -->
<div class="p-4 sm:p-6 md:p-8 lg:p-12">
  <!-- Padding increases with screen size -->
</div>

<!-- Hide/show at breakpoints -->
<div class="hidden md:block">Only visible on medium screens and up</div>
<div class="md:hidden">Only visible on small screens</div>

State Modifiers

Hover, Focus, Active

<!-- Hover -->
<button class="bg-blue-500 hover:bg-blue-700 text-white">
  Hover me
</button>

<!-- Focus -->
<input class="border focus:border-blue-500 focus:ring-2 focus:ring-blue-200 outline-none" />

<!-- Active -->
<button class="bg-blue-500 active:bg-blue-800 text-white">
  Click me
</button>

<!-- Focus-within (when child is focused) -->
<div class="border focus-within:border-blue-500">
  <input class="outline-none" />
</div>

<!-- Disabled -->
<button class="bg-blue-500 disabled:bg-gray-300 disabled:cursor-not-allowed" disabled>
  Disabled
</button>

<!-- Group hover -->
<div class="group">
  <h3 class="text-gray-700 group-hover:text-blue-500">Card Title</h3>
  <p class="text-gray-500 group-hover:text-gray-700">Card content</p>
</div>

<!-- Peer modifiers -->
<input type="checkbox" class="peer" />
<label class="peer-checked:text-blue-500">Checkbox label</label>

<!-- Placeholder -->
<input class="placeholder:text-gray-400 placeholder:italic" placeholder="Enter text..." />

<!-- First/last child -->
<ul>
  <li class="first:pt-0 last:pb-0 py-2">Item 1</li>
  <li class="first:pt-0 last:pb-0 py-2">Item 2</li>
  <li class="first:pt-0 last:pb-0 py-2">Item 3</li>
</ul>

<!-- Odd/even -->
<tbody>
  <tr class="odd:bg-white even:bg-gray-50"><td>Row 1</td></tr>
  <tr class="odd:bg-white even:bg-gray-50"><td>Row 2</td></tr>
</tbody>

Component Examples

Button Variants

<!-- Primary button -->
<button class="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-4 rounded-lg transition-colors">
  Primary
</button>

<!-- Secondary button -->
<button class="bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-2 px-4 rounded-lg transition-colors">
  Secondary
</button>

<!-- Outline button -->
<button class="border-2 border-blue-500 text-blue-500 hover:bg-blue-500 hover:text-white font-medium py-2 px-4 rounded-lg transition-colors">
  Outline
</button>

<!-- Ghost button -->
<button class="text-blue-500 hover:bg-blue-50 font-medium py-2 px-4 rounded-lg transition-colors">
  Ghost
</button>

<!-- Button sizes -->
<button class="text-sm py-1 px-3 ...">Small</button>
<button class="py-2 px-4 ...">Medium</button>
<button class="text-lg py-3 px-6 ...">Large</button>

<!-- Button with icon -->
<button class="flex items-center gap-2 bg-blue-500 text-white py-2 px-4 rounded-lg">
  <svg class="w-5 h-5" ...></svg>
  <span>With Icon</span>
</button>

Card Component

<!-- Basic card -->
<div class="bg-white rounded-xl shadow-lg overflow-hidden">
  <img class="w-full h-48 object-cover" src="image.jpg" alt="Card image" />
  <div class="p-6">
    <span class="text-sm text-blue-500 font-medium">Category</span>
    <h3 class="text-xl font-bold text-gray-800 mt-1">Card Title</h3>
    <p class="text-gray-600 mt-2">
      Card description goes here. This is a sample card component.
    </p>
    <div class="flex items-center justify-between mt-4">
      <span class="text-gray-500 text-sm">5 min read</span>
      <button class="text-blue-500 hover:text-blue-700 font-medium">
        Read more →
      </button>
    </div>
  </div>
</div>

<!-- Card grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
  <!-- Card 1, 2, 3... -->
</div>

Form Elements

<!-- Input field -->
<div class="mb-4">
  <label class="block text-gray-700 font-medium mb-2">Email</label>
  <input
    type="email"
    class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition-all"
    placeholder="Enter your email"
  />
</div>

<!-- Input with error -->
<div class="mb-4">
  <label class="block text-gray-700 font-medium mb-2">Password</label>
  <input
    type="password"
    class="w-full px-4 py-2 border border-red-500 rounded-lg focus:ring-2 focus:ring-red-500 outline-none"
  />
  <p class="text-red-500 text-sm mt-1">Password is required</p>
</div>

<!-- Select -->
<select class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 outline-none">
  <option>Option 1</option>
  <option>Option 2</option>
</select>

<!-- Checkbox -->
<label class="flex items-center gap-2 cursor-pointer">
  <input type="checkbox" class="w-5 h-5 rounded border-gray-300 text-blue-500 focus:ring-blue-500" />
  <span class="text-gray-700">Remember me</span>
</label>

<!-- Toggle switch (custom) -->
<label class="relative inline-flex items-center cursor-pointer">
  <input type="checkbox" class="sr-only peer" />
  <div class="w-11 h-6 bg-gray-200 peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:bg-blue-500 after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all"></div>
</label>

Navigation

<!-- Navbar -->
<nav class="bg-white shadow-sm">
  <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
    <div class="flex justify-between h-16">
      <div class="flex items-center">
        <img class="h-8 w-auto" src="logo.svg" alt="Logo" />
      </div>

      <div class="hidden md:flex items-center space-x-8">
        <a href="#" class="text-gray-900 hover:text-blue-500 font-medium">Home</a>
        <a href="#" class="text-gray-500 hover:text-blue-500 font-medium">About</a>
        <a href="#" class="text-gray-500 hover:text-blue-500 font-medium">Services</a>
        <a href="#" class="text-gray-500 hover:text-blue-500 font-medium">Contact</a>
      </div>

      <div class="flex items-center">
        <button class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-lg">
          Sign In
        </button>
      </div>
    </div>
  </div>
</nav>

Customization

tailwind.config.js

// tailwind.config.js
module.exports = {
  content: ['./src/**/*.{js,jsx,ts,tsx}'],
  theme: {
    // Override defaults
    screens: {
      sm: '640px',
      md: '768px',
      lg: '1024px',
      xl: '1280px',
    },

    // Extend defaults
    extend: {
      colors: {
        primary: {
          50: '#eff6ff',
          100: '#dbeafe',
          500: '#3b82f6',
          600: '#2563eb',
          700: '#1d4ed8',
        },
        brand: '#FF5A5F',
      },

      fontFamily: {
        sans: ['Inter', 'system-ui', 'sans-serif'],
        display: ['Poppins', 'sans-serif'],
      },

      spacing: {
        '128': '32rem',
        '144': '36rem',
      },

      borderRadius: {
        '4xl': '2rem',
      },

      animation: {
        'spin-slow': 'spin 3s linear infinite',
        'bounce-slow': 'bounce 2s infinite',
      },

      keyframes: {
        wiggle: {
          '0%, 100%': { transform: 'rotate(-3deg)' },
          '50%': { transform: 'rotate(3deg)' },
        },
      },
    },
  },
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
    require('@tailwindcss/aspect-ratio'),
  ],
};

Using @apply for Component Classes

/* src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer components {
  .btn {
    @apply py-2 px-4 font-medium rounded-lg transition-colors;
  }

  .btn-primary {
    @apply btn bg-blue-500 text-white hover:bg-blue-600;
  }

  .btn-secondary {
    @apply btn bg-gray-200 text-gray-800 hover:bg-gray-300;
  }

  .input {
    @apply w-full px-4 py-2 border border-gray-300 rounded-lg
           focus:ring-2 focus:ring-blue-500 focus:border-transparent
           outline-none transition-all;
  }

  .card {
    @apply bg-white rounded-xl shadow-lg overflow-hidden;
  }
}

@layer utilities {
  .text-balance {
    text-wrap: balance;
  }
}

Tailwind CSS with React

// Using className
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-800',
    outline: 'border-2 border-blue-500 text-blue-500 hover:bg-blue-500 hover:text-white',
  };

  return (
    <button className={`py-2 px-4 rounded-lg font-medium transition-colors ${variants[variant]}`}>
      {children}
    </button>
  );
}

// Using clsx/classnames for conditional classes
import clsx from 'clsx';

function Alert({ type, message }) {
  return (
    <div
      className={clsx(
        'p-4 rounded-lg',
        {
          'bg-green-100 text-green-800': type === 'success',
          'bg-red-100 text-red-800': type === 'error',
          'bg-yellow-100 text-yellow-800': type === 'warning',
          'bg-blue-100 text-blue-800': type === 'info',
        }
      )}
    >
      {message}
    </div>
  );
}

// tailwind-merge for overriding classes
import { twMerge } from 'tailwind-merge';

function Button({ className, ...props }) {
  return (
    <button
      className={twMerge(
        'bg-blue-500 text-white py-2 px-4 rounded',
        className // Can override base classes
      )}
      {...props}
    />
  );
}

// Usage
<Button className="bg-red-500">Red Button</Button>

Key Takeaways

  • Tailwind uses utility classes instead of custom CSS
  • Mobile-first responsive design with sm, md, lg, xl breakpoints
  • State modifiers like hover:, focus:, active: for interactions
  • Flexbox and Grid utilities for layouts
  • Customize with tailwind.config.js for colors, fonts, spacing
  • Use @apply to create reusable component classes
  • Use clsx/classnames for conditional class logic in React
  • Use tailwind-merge to safely override classes