Tailwind CSS Utility-First Approach

Module 7: CSS Preprocessors & Frameworks

Introduction to Tailwind CSS

Tailwind CSS is a utility-first CSS framework that allows you to build custom designs without ever leaving your HTML. Unlike component-based frameworks like Bootstrap, Tailwind doesn't provide pre-designed components. Instead, it gives you low-level utility classes that you can compose to build any design directly in your markup.

graph TD A[Tailwind CSS] --> B[Design System] A --> C[Utility Classes] A --> D[Build Process] A --> E[Plugins] B --> B1[Color Palette] B --> B2[Spacing Scale] B --> B3[Typography Scale] B --> B4[Breakpoints] B --> B5[Shadows] C --> C1[Layout] C --> C2[Typography] C --> C3[Colors] C --> C4[Spacing] C --> C5[Flexbox] C --> C6[Grid] C --> C7[Many more...] D --> D1[PurgeCSS Integration] D --> D2[JIT Compiler] D --> D3[PostCSS] E --> E1[Official Plugins] E --> E2[Community Plugins] E --> E3[Custom Plugins]

The Utility-First Philosophy

The utility-first approach is fundamentally different from how many developers have traditionally written CSS:

Traditional Approach

/* styles.css */
.card {
  background-color: white;
  border-radius: 0.5rem;
  padding: 1.5rem;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.card-title {
  font-size: 1.25rem;
  font-weight: 600;
  margin-bottom: 0.75rem;
}

/* HTML */
<div class="card">
  <h2 class="card-title">Card Title</h2>
  <p>Card content goes here.</p>
</div>

Tailwind CSS Approach

<!-- No separate CSS file needed -->
<div class="bg-white rounded-lg p-6 shadow-md">
  <h2 class="text-xl font-semibold mb-3">Card Title</h2>
  <p>Card content goes here.</p>
</div>

Analogy: Tailwind CSS as LEGO Bricks

Think of Tailwind CSS as a box of LEGO bricks versus a pre-assembled toy:

  • Component Frameworks (Bootstrap) are like pre-assembled toys — ready-to-use but limited in customization options.
  • Tailwind CSS is like a collection of LEGO bricks — simple, atomic pieces that can be assembled in countless ways to build exactly what you want.

With LEGO, you build by connecting small standardized pieces rather than starting with a finished product. Similarly, with Tailwind, you build interfaces by combining small utility classes rather than writing custom CSS or overriding pre-designed components.

Getting Started with Tailwind CSS

Installation Methods

There are several ways to add Tailwind CSS to your project:

Method 1: Using NPM

The recommended way to install Tailwind CSS:

# Create a new project directory
mkdir tailwind-project
cd tailwind-project

# Initialize a new package.json
npm init -y

# Install Tailwind CSS and its peer dependencies
npm install -D tailwindcss postcss autoprefixer

# Generate the Tailwind config file
npx tailwindcss init

# Create a postcss.config.js file
echo "module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  }
}" > postcss.config.js

# Create a CSS file that includes Tailwind's directives
mkdir -p src
echo "@tailwind base;
@tailwind components;
@tailwind utilities;" > src/input.css

# Build the CSS
npx tailwindcss -i src/input.css -o dist/output.css

Method 2: CDN (for prototyping only)

Not recommended for production but useful for quick demos:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Tailwind CSS Demo</title>
  <script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
  <div class="p-6 max-w-sm mx-auto bg-white rounded-xl shadow-md flex items-center space-x-4">
    <div>
      <div class="text-xl font-medium text-black">Hello Tailwind CSS!</div>
      <p class="text-gray-500">Using the CDN version for quick prototyping.</p>
    </div>
  </div>
</body>
</html>

Method 3: Framework-Specific Installation

For projects using popular frameworks:

# Next.js
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

# React (Create React App)
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

# Vue (Vue CLI)
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

# Angular
ng add @ngneat/tailwind

Configuring Tailwind CSS

Tailwind's behavior is configured in the tailwind.config.js file:

// tailwind.config.js
module.exports = {
  // Which files Tailwind should scan for classes
  content: [
    "./src/**/*.{html,js,jsx,ts,tsx}",
  ],
  
  // Toggle dark mode feature
  darkMode: 'class', // or 'media'
  
  // Customizing the design system
  theme: {
    // Extending the default theme
    extend: {
      colors: {
        'brand-blue': '#1992d4',
        'brand-purple': '#9061f9',
      },
      spacing: {
        '72': '18rem',
        '84': '21rem',
        '96': '24rem',
      },
      borderRadius: {
        'xl': '1rem',
        '2xl': '2rem',
      },
    },
  },
  
  // Enable/disable specific core plugins
  corePlugins: {
    float: false, // disables float utilities
  },
  
  // Add third-party or custom plugins
  plugins: [
    require('@tailwindcss/forms'),
    require('@tailwindcss/typography'),
  ],
}

Setting Up a Basic Project

Let's set up a simple Tailwind CSS project structure:

tailwind-project/
├── node_modules/
├── src/
│   ├── input.css  # Contains Tailwind directives
│   └── index.html # Main HTML file
├── dist/
│   ├── output.css # Compiled CSS
│   └── index.html # Copy of HTML for production
├── package.json
├── postcss.config.js
└── tailwind.config.js

Basic package.json scripts to build your CSS:

"scripts": {
  "dev": "tailwindcss -i src/input.css -o dist/output.css --watch",
  "build": "tailwindcss -i src/input.css -o dist/output.css --minify"
}

Tailwind CSS Core Concepts

Utility Class Naming System

Tailwind uses a consistent naming convention that makes its utility classes intuitive and predictable:

graph LR A["Utility Class Pattern"] --> B["property-value"] B --> C["p-4"] B --> D["text-lg"] B --> E["bg-blue-500"] F["Responsive Prefixes"] --> G["breakpoint:property-value"] G --> H["md:p-6"] G --> I["lg:text-xl"] J["State Variants"] --> K["state:property-value"] K --> L["hover:bg-blue-700"] K --> M["focus:ring-2"]
Category Prefix Example CSS Equivalent
Padding p-, px-, py-, pt-, pr-, pb-, pl- p-4 padding: 1rem;
Margin m-, mx-, my-, mt-, mr-, mb-, ml- mt-2 margin-top: 0.5rem;
Width/Height w-, h- w-1/2 width: 50%;
Font Size text- text-lg font-size: 1.125rem; line-height: 1.75rem;
Text Color text- text-blue-500 color: #3b82f6;
Background Color bg- bg-gray-100 background-color: #f3f4f6;
Border border, border-t, etc. border-2 border-width: 2px;
Flexbox flex, flex-row, etc. flex items-center display: flex; align-items: center;

Responsive Design System

Tailwind uses a mobile-first responsive design system with breakpoint prefixes:

Prefix Min Width Example CSS Equivalent
(none) 0px (default) p-4 padding: 1rem;
sm: 640px sm:p-6 @media (min-width: 640px) { padding: 1.5rem; }
md: 768px md:p-8 @media (min-width: 768px) { padding: 2rem; }
lg: 1024px lg:p-10 @media (min-width: 1024px) { padding: 2.5rem; }
xl: 1280px xl:p-12 @media (min-width: 1280px) { padding: 3rem; }
2xl: 1536px 2xl:p-16 @media (min-width: 1536px) { padding: 4rem; }

Responsive Design Example

<div class="w-full md:w-1/2 lg:w-1/3 p-4 md:p-6">
  <!-- 
    - Full width on mobile
    - Half width on medium screens (md:w-1/2)
    - One-third width on large screens (lg:w-1/3)
    - 1rem padding on mobile (p-4)
    - 1.5rem padding on medium and up (md:p-6)
  -->
  Content here
</div>

State Variants

Tailwind provides state variants for hover, focus, active, and other states:

<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:ring-2 focus:ring-blue-400 focus:ring-opacity-50">
  Hover and Focus Me
</button>
Variant Example Description
hover: hover:bg-blue-700 Applied when element is hovered
focus: focus:outline-none Applied when element is focused
active: active:bg-blue-800 Applied when element is active
disabled: disabled:opacity-50 Applied when element is disabled
dark: dark:bg-gray-800 Applied in dark mode
group-hover: group-hover:text-white Applied when parent with class "group" is hovered

Building Common UI Components with Tailwind

Buttons

<!-- Primary Button -->
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
  Primary Button
</button>

<!-- Secondary Button -->
<button class="bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded">
  Secondary Button
</button>

<!-- Disabled Button -->
<button class="bg-blue-500 text-white font-bold py-2 px-4 rounded opacity-50 cursor-not-allowed">
  Disabled Button
</button>

<!-- Success Button -->
<button class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded">
  Success Button
</button>

<!-- Danger Button -->
<button class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">
  Danger Button
</button>

<!-- Small Button -->
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-2 text-sm rounded">
  Small Button
</button>

<!-- Large Button -->
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-3 px-6 text-lg rounded">
  Large Button
</button>

Cards

<!-- Simple Card -->
<div class="max-w-sm rounded overflow-hidden shadow-lg">
  <img class="w-full" src="https://source.unsplash.com/random/400x200" alt="Card image">
  <div class="px-6 py-4">
    <div class="font-bold text-xl mb-2">Card Title</div>
    <p class="text-gray-700 text-base">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    </p>
  </div>
  <div class="px-6 pt-4 pb-2">
    <span class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2">#photography</span>
    <span class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-semibold text-gray-700 mr-2 mb-2">#travel</span>
  </div>
</div>

<!-- Horizontal Card -->
<div class="max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl">
  <div class="md:flex">
    <div class="md:flex-shrink-0">
      <img class="h-48 w-full object-cover md:w-48" src="https://source.unsplash.com/random/200x200" alt="Card image">
    </div>
    <div class="p-8">
      <div class="uppercase tracking-wide text-sm text-indigo-500 font-semibold">Category</div>
      <a href="#" class="block mt-1 text-lg leading-tight font-medium text-black hover:underline">Card Title</a>
      <p class="mt-2 text-gray-500">Card description goes here, providing more details about the content.</p>
    </div>
  </div>
</div>

Forms

<form class="max-w-md mx-auto bg-white p-6 rounded-lg shadow-md">
  <div class="mb-4">
    <label class="block text-gray-700 text-sm font-bold mb-2" for="username">
      Username
    </label>
    <input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="username" type="text" placeholder="Username">
  </div>
  
  <div class="mb-6">
    <label class="block text-gray-700 text-sm font-bold mb-2" for="password">
      Password
    </label>
    <input class="shadow appearance-none border border-red-500 rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline" id="password" type="password" placeholder="******************">
    <p class="text-red-500 text-xs italic">Please choose a password.</p>
  </div>
  
  <div class="mb-6">
    <label class="block text-gray-700 text-sm font-bold mb-2">
      Options
    </label>
    <div class="mt-2">
      <div class="flex items-center">
        <input id="option1" name="option" type="radio" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500">
        <label for="option1" class="ml-3 block text-sm font-medium text-gray-700">
          Option 1
        </label>
      </div>
      <div class="flex items-center mt-2">
        <input id="option2" name="option" type="radio" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500">
        <label for="option2" class="ml-3 block text-sm font-medium text-gray-700">
          Option 2
        </label>
      </div>
    </div>
  </div>
  
  <div class="flex items-center justify-between">
    <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" type="button">
      Sign In
    </button>
    <a class="inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800" href="#">
      Forgot Password?
    </a>
  </div>
</form>

Navigation Bar

<nav class="bg-white shadow">
  <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">
        <div class="flex-shrink-0 flex items-center">
          <img class="h-8 w-auto" src="https://tailwindui.com/img/logos/workflow-mark-indigo-600.svg" alt="Logo">
        </div>
        <div class="hidden sm:ml-6 sm:flex sm:space-x-8">
          <!-- Desktop Navigation -->
          <a href="#" class="border-indigo-500 text-gray-900 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
            Dashboard
          </a>
          <a href="#" class="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
            Team
          </a>
          <a href="#" class="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
            Projects
          </a>
          <a href="#" class="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
            Calendar
          </a>
        </div>
      </div>
      
      <!-- Mobile menu button -->
      <div class="-mr-2 flex items-center sm:hidden">
        <button type="button" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500" aria-expanded="false">
          <span class="sr-only">Open main menu</span>
          <!-- Heroicon name: menu -->
          <svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
          </svg>
        </button>
      </div>
    </div>
  </div>
  
  <!-- Mobile menu -->
  <div class="hidden sm:hidden">
    <div class="pt-2 pb-3 space-y-1">
      <a href="#" class="bg-indigo-50 border-indigo-500 text-indigo-700 block pl-3 pr-4 py-2 border-l-4 text-base font-medium">
        Dashboard
      </a>
      <a href="#" class="border-transparent text-gray-500 hover:bg-gray-50 hover:border-gray-300 hover:text-gray-700 block pl-3 pr-4 py-2 border-l-4 text-base font-medium">
        Team
      </a>
      <a href="#" class="border-transparent text-gray-500 hover:bg-gray-50 hover:border-gray-300 hover:text-gray-700 block pl-3 pr-4 py-2 border-l-4 text-base font-medium">
        Projects
      </a>
      <a href="#" class="border-transparent text-gray-500 hover:bg-gray-50 hover:border-gray-300 hover:text-gray-700 block pl-3 pr-4 py-2 border-l-4 text-base font-medium">
        Calendar
      </a>
    </div>
  </div>
</nav>

Modern Layout Techniques with Tailwind

Flexbox Layout

<!-- Basic Flexbox Container -->
<div class="flex">
  <div class="flex-1 bg-blue-100 p-4">Flex Item 1</div>
  <div class="flex-1 bg-blue-200 p-4">Flex Item 2</div>
  <div class="flex-1 bg-blue-300 p-4">Flex Item 3</div>
</div>

<!-- Centered Content (Horizontal and Vertical) -->
<div class="flex justify-center items-center h-64 bg-gray-100">
  <div class="bg-white p-6 rounded shadow">Centered Content</div>
</div>

<!-- Navigation Bar with Flexbox -->
<nav class="flex items-center justify-between bg-indigo-600 p-4">
  <div class="text-white font-bold">Logo</div>
  <div class="flex space-x-4">
    <a href="#" class="text-white hover:text-indigo-200">Home</a>
    <a href="#" class="text-white hover:text-indigo-200">About</a>
    <a href="#" class="text-white hover:text-indigo-200">Services</a>
    <a href="#" class="text-white hover:text-indigo-200">Contact</a>
  </div>
</nav>

Grid Layout

<!-- Basic Grid -->
<div class="grid grid-cols-3 gap-4">
  <div class="bg-blue-100 p-4">Grid Item 1</div>
  <div class="bg-blue-200 p-4">Grid Item 2</div>
  <div class="bg-blue-300 p-4">Grid Item 3</div>
  <div class="bg-blue-400 p-4">Grid Item 4</div>
  <div class="bg-blue-500 p-4">Grid Item 5</div>
  <div class="bg-blue-600 p-4">Grid Item 6</div>
</div>

<!-- Responsive Grid -->
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
  <div class="bg-blue-100 p-4">Responsive Item 1</div>
  <div class="bg-blue-200 p-4">Responsive Item 2</div>
  <div class="bg-blue-300 p-4">Responsive Item 3</div>
  <div class="bg-blue-400 p-4">Responsive Item 4</div>
  <div class="bg-blue-500 p-4">Responsive Item 5</div>
  <div class="bg-blue-600 p-4">Responsive Item 6</div>
  <div class="bg-blue-700 p-4">Responsive Item 7</div>
  <div class="bg-blue-800 p-4">Responsive Item 8</div>
</div>

<!-- Grid with Specific Item Placement -->
<div class="grid grid-cols-3 gap-4">
  <div class="col-span-2 bg-green-100 p-4">Spans 2 columns</div>
  <div class="bg-green-200 p-4">Spans 1 column</div>
  <div class="bg-green-300 p-4">Spans 1 column</div>
  <div class="col-span-2 bg-green-400 p-4">Spans 2 columns</div>
</div>

Responsive Design and Container Layouts

<!-- Responsive container with max-width at various breakpoints -->
<div class="container mx-auto px-4">
  <!-- Content here -->
</div>

<!-- Responsive column layout -->
<div class="container mx-auto px-4">
  <div class="flex flex-wrap -mx-4">
    <!-- Full width on mobile, half width on tablets, one-third on desktop -->
    <div class="w-full md:w-1/2 lg:w-1/3 px-4 mb-8">
      <div class="bg-white rounded shadow p-6">
        <h2 class="text-xl font-bold mb-2">Card Title 1</h2>
        <p class="text-gray-700">Card content goes here.</p>
      </div>
    </div>
    
    <div class="w-full md:w-1/2 lg:w-1/3 px-4 mb-8">
      <div class="bg-white rounded shadow p-6">
        <h2 class="text-xl font-bold mb-2">Card Title 2</h2>
        <p class="text-gray-700">Card content goes here.</p>
      </div>
    </div>
    
    <div class="w-full md:w-1/2 lg:w-1/3 px-4 mb-8">
      <div class="bg-white rounded shadow p-6">
        <h2 class="text-xl font-bold mb-2">Card Title 3</h2>
        <p class="text-gray-700">Card content goes here.</p>
      </div>
    </div>
  </div>
</div>

Real-World Example: Product Page Layout

<!-- Product Page with Tailwind CSS -->
<div class="bg-white">
  <!-- Navigation -->
  <nav class="bg-gray-800">
    <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
      <div class="flex items-center justify-between h-16">
        <div class="flex items-center">
          <div class="flex-shrink-0">
            <img class="h-8 w-8" src="logo.svg" alt="Logo">
          </div>
          <div class="hidden md:block">
            <div class="ml-10 flex items-baseline space-x-4">
              <a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Home</a>
              <a href="#" class="bg-gray-900 text-white px-3 py-2 rounded-md text-sm font-medium">Products</a>
              <a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">About</a>
              <a href="#" class="text-gray-300 hover:bg-gray-700 hover:text-white px-3 py-2 rounded-md text-sm font-medium">Contact</a>
            </div>
          </div>
        </div>
        <div class="flex items-center">
          <button class="bg-gray-800 p-1 rounded-full text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-800 focus:ring-white">
            <span class="sr-only">View cart</span>
            <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z" />
            </svg>
          </button>
        </div>
      </div>
    </div>
  </nav>

  <!-- Product Section -->
  <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
    <div class="lg:grid lg:grid-cols-2 lg:gap-x-8">
      <!-- Product Image -->
      <div class="lg:max-w-lg lg:self-end">
        <div class="rounded-lg overflow-hidden">
          <img src="product.jpg" alt="Product image" class="w-full h-full object-center object-cover">
        </div>
        <div class="mt-4 grid grid-cols-4 gap-2">
          <div class="col-span-1 aspect-w-1 aspect-h-1">
            <img src="thumbnail1.jpg" alt="Thumbnail 1" class="w-full h-full object-center object-cover rounded-md cursor-pointer border hover:border-indigo-500">
          </div>
          <div class="col-span-1 aspect-w-1 aspect-h-1">
            <img src="thumbnail2.jpg" alt="Thumbnail 2" class="w-full h-full object-center object-cover rounded-md cursor-pointer border hover:border-indigo-500">
          </div>
          <div class="col-span-1 aspect-w-1 aspect-h-1">
            <img src="thumbnail3.jpg" alt="Thumbnail 3" class="w-full h-full object-center object-cover rounded-md cursor-pointer border hover:border-indigo-500">
          </div>
          <div class="col-span-1 aspect-w-1 aspect-h-1">
            <img src="thumbnail4.jpg" alt="Thumbnail 4" class="w-full h-full object-center object-cover rounded-md cursor-pointer border hover:border-indigo-500">
          </div>
        </div>
      </div>

      <!-- Product Details -->
      <div class="mt-10 lg:mt-0 lg:max-w-lg lg:self-start">
        <div class="flex justify-between">
          <h1 class="text-3xl font-extrabold tracking-tight text-gray-900">Premium Headphones</h1>
          <p class="text-3xl font-bold text-gray-900">$299.99</p>
        </div>
        
        <div class="mt-4">
          <div class="flex items-center">
            <!-- Stars -->
            <div class="flex text-yellow-400">
              <svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20">
                <path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
              </svg>
              <!-- Repeat for 5 stars -->
            </div>
            <p class="ml-3 text-sm text-gray-700">4.9 (128 reviews)</p>
          </div>
        </div>

        <div class="mt-6">
          <h2 class="text-sm font-medium text-gray-900">Description</h2>
          <div class="mt-2 space-y-6 text-base text-gray-700">
            <p>Experience premium sound quality with our wireless noise-cancelling headphones. These headphones feature the latest Bluetooth technology, 40-hour battery life, and cushioned ear cups for maximum comfort.</p>
          </div>
        </div>

        <div class="mt-8">
          <h2 class="text-sm font-medium text-gray-900">Color</h2>
          <div class="mt-2 flex items-center space-x-3">
            <div class="h-8 w-8 rounded-full border bg-black cursor-pointer border-gray-900 ring-2 ring-indigo-500"></div>
            <div class="h-8 w-8 rounded-full border bg-gray-200 cursor-pointer"></div>
            <div class="h-8 w-8 rounded-full border bg-indigo-500 cursor-pointer"></div>
          </div>
        </div>

        <div class="mt-10">
          <button class="w-full bg-indigo-600 border border-transparent rounded-md py-3 px-8 flex items-center justify-center text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
            Add to cart
          </button>
          <div class="mt-6 text-center">
            <a href="#" class="text-sm font-medium text-indigo-600 hover:text-indigo-500">View full details</a>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

Extending and Customizing Tailwind

Extracting Components with @apply

For repeated patterns, you can extract components using the @apply directive in your CSS:

/* In your CSS file */
.btn {
  @apply inline-block px-4 py-2 rounded font-semibold;
}

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

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

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

.card-body {
  @apply p-6;
}

/* HTML usage */
<button class="btn btn-primary">Primary Button</button>
<button class="btn btn-secondary">Secondary Button</button>

<div class="card">
  <div class="card-body">
    <h3 class="text-lg font-bold mb-2">Card Title</h3>
    <p>Card content...</p>
  </div>
</div>

Creating Custom Plugins

For more complex extensions, you can create custom plugins:

// tailwind.config.js
const plugin = require('tailwindcss/plugin')

module.exports = {
  // ... other config
  plugins: [
    plugin(function({ addComponents, theme }) {
      const buttons = {
        '.btn': {
          padding: `${theme('spacing.2')} ${theme('spacing.4')}`,
          borderRadius: theme('borderRadius.md'),
          fontWeight: theme('fontWeight.semibold'),
        },
        '.btn-primary': {
          backgroundColor: theme('colors.blue.500'),
          color: theme('colors.white'),
          '&:hover': {
            backgroundColor: theme('colors.blue.700'),
          },
        },
        '.btn-secondary': {
          backgroundColor: theme('colors.gray.200'),
          color: theme('colors.gray.800'),
          '&:hover': {
            backgroundColor: theme('colors.gray.300'),
          },
        },
      }

      addComponents(buttons)
    }),
  ],
}

Customizing the Theme

You can customize Tailwind's default theme to match your design system:

// tailwind.config.js
const colors = require('tailwindcss/colors')

module.exports = {
  theme: {
    // Completely replace the default colors
    colors: {
      transparent: 'transparent',
      current: 'currentColor',
      black: '#000',
      white: '#fff',
      gray: colors.coolGray,
      primary: {
        light: '#63b3ed',
        DEFAULT: '#3182ce',
        dark: '#2c5282',
      },
      secondary: {
        light: '#e9d8fd',
        DEFAULT: '#9f7aea',
        dark: '#6b46c1',
      },
    },
    
    // Extend the default theme
    extend: {
      // Add new spacing values
      spacing: {
        '72': '18rem',
        '84': '21rem',
        '96': '24rem',
        '128': '32rem',
      },
      
      // Add custom font families
      fontFamily: {
        sans: ['Inter var', 'sans-serif'],
        display: ['Poppins', 'sans-serif'],
      },
      
      // Add custom animation values
      animation: {
        'bounce-slow': 'bounce 3s infinite',
      },
      
      // Add custom border radius values
      borderRadius: {
        'xl': '1rem',
        '2xl': '2rem',
      },
    },
  },
}

Responsive Variants

You can customize which features are responsive by modifying the variants configuration:

// tailwind.config.js
module.exports = {
  // ... other config
  variants: {
    extend: {
      // Enable hover and focus variants for backgroundColor
      backgroundColor: ['hover', 'focus', 'active', 'disabled'],
      
      // Enable responsive variants for typography
      textColor: ['responsive', 'hover', 'focus', 'dark'],
      
      // Add dark mode variants for specific utilities
      borderWidth: ['responsive', 'dark'],
    },
  },
}

Tailwind CSS Best Practices

Organization Strategies

Performance Optimization

Common Pitfalls to Avoid

Example: Improving a Bloated Component

/* Before: Too many utility classes */
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline focus:ring-2 focus:ring-blue-400 focus:ring-opacity-50 transition duration-300 ease-in-out transform hover:-translate-y-1 hover:scale-105 active:bg-blue-800">
  Click Me
</button>

/* After: Extract to a component class */
/* In your CSS */
.btn-blue {
  @apply bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline focus:ring-2 focus:ring-blue-400 focus:ring-opacity-50 transition duration-300 ease-in-out transform hover:-translate-y-1 hover:scale-105 active:bg-blue-800;
}

/* In your HTML */
<button class="btn-blue">
  Click Me
</button>

Comparing Tailwind vs. Bootstrap Approach

Philosophical Differences

Aspect Tailwind CSS Bootstrap
Design Philosophy Utility-first, low-level classes Component-first, pre-designed elements
Default Look No default styling, build your own design Distinct "Bootstrap look" out of the box
Learning Curve Steeper initially, easier to customize Easier initially, harder to customize
Bundle Size Small after purging (10-30KB) Larger even after customization (50-150KB)
Customization Highly customizable through config Customizable through Sass variables
JavaScript No JS components included Includes JS components

Same Component, Different Approaches

Bootstrap Card

<div class="card" style="width: 18rem;">
  <img src="..." class="card-img-top" alt="...">
  <div class="card-body">
    <h5 class="card-title">Card title</h5>
    <p class="card-text">Some quick example text to build on the card title.</p>
    <a href="#" class="btn btn-primary">Go somewhere</a>
  </div>
</div>

Tailwind CSS Card

<div class="max-w-sm rounded overflow-hidden shadow-lg">
  <img class="w-full" src="..." alt="...">
  <div class="px-6 py-4">
    <h5 class="font-bold text-xl mb-2">Card title</h5>
    <p class="text-gray-700 text-base">Some quick example text to build on the card title.</p>
    <a href="#" class="inline-block mt-4 px-4 py-2 bg-blue-500 text-white font-semibold rounded hover:bg-blue-700">Go somewhere</a>
  </div>
</div>

When to Choose Tailwind Over Bootstrap

When to Choose Bootstrap Over Tailwind

Tailwind Ecosystem

Official Tailwind Plugins

Plugin Description Installation
@tailwindcss/forms A plugin that provides a basic reset for form styles npm install @tailwindcss/forms
@tailwindcss/typography A plugin that provides a set of typography utilities npm install @tailwindcss/typography
@tailwindcss/aspect-ratio A plugin for controlling aspect ratio of elements npm install @tailwindcss/aspect-ratio
@tailwindcss/line-clamp A plugin for truncating text to a specific number of lines npm install @tailwindcss/line-clamp

Popular Tailwind UI Component Libraries

Development Tools

Practice Activities

  1. Tailwind Card Component: Create a product card component using Tailwind CSS that includes an image, title, description, price, and a "Buy Now" button. Make it responsive for mobile, tablet, and desktop screens.
  2. NavBar Implementation: Build a responsive navigation bar with a logo, navigation links, and a mobile menu hamburger icon. The menu should collapse on mobile screens and expand on larger screens.
  3. Bootstrap to Tailwind Conversion: Take a simple Bootstrap component (like an alert or a form) and convert it to Tailwind CSS. Compare the HTML structure and class naming differences.
  4. Custom Theme Configuration: Create a custom color palette and spacing scale in a Tailwind configuration file. Then build a simple UI component that uses your custom theme values.
  5. Component Extraction: Create a set of reusable UI components using the @apply directive in a CSS file. Components should include buttons, cards, and form elements with different variants.

Additional Resources

Key Takeaways

Next Module

In our next module, we'll explore JavaScript Fundamentals, starting with JavaScript History and Evolution, the JavaScript Execution Environment, and working with the Browser Console.