Menu

Tailwind CSS Introduction

Tailwind CSS is a utility-first framework that offers a comprehensive set of low-level utility classes. These classes can be applied directly within HTML or JSX, eliminating the need for writing custom CSS and enable you to build highly flexible and scalable user interfaces with ease.

In CSS, you have to create separate stylesheets, define class names, and manually maintain a growing list of styles. Tailwind eliminates this effort by providing a structured set of utility classes that handle the styling. These utility classes cover a wide range of styling needs, including layout (flex, grid, block), spacing (p-4, m-2, gap-6), typography (text-lg, font-bold, leading-relaxed) and many more.

This means you no longer have to worry about naming conventions, selector specificity, or maintaining massive CSS files. Everything is handled inline, keeping styles modular and easy to manage.

Features of Tailwind CSS

Utility-First Approach

A utility class is a single-purpose CSS class that applies a specific style directly to an element, such as text-center for centering text or p-4 for adding padding. Instead of writing custom CSS rules in separate stylesheets, developers use these predefined classes to compose styles inline within their HTML or JSX.

<button className="bg-blue-500 text-white px-4 py-2 rounded-md">Click Me</button>

Utility classes in Tailwind CSS follow an atomic design principle, where each class does only one thing. Since each class applies only one style property, there are no unintended side effects when modifying styles. For example, changing the background color of one button does not affect the other as styles are applied directly to the element.

Built-in Responsiveness

Tailwind CSS has a built-in responsive design system to apply styles only on a particular breakpoint. Instead of media queries, it uses responsive variants (sm:, md:, lg:, xl:, 2xl:) to adjust styles based on screen size, e.g., md:p-4, lg:flex-col, etc. Tailwind also follows a mobile-first approach, where styles apply to the smallest screen by default and are overridden for larger screens.

This eliminates the need for custom media queries. For example, instead of writing:


@media (min-width: 768px) {
  .hidden {
    display: none;
  }
}

You can write the following code to achieve the same behavior:

<div className="md:hidden">Hidden Text</div>

Tailwind’s predefined responsive variants ensure consistency by enforcing a structured design system across all components. This also reduces CSS complexity by eliminating the need for custom media queries, making layouts easier to scale and maintain.

Dark Mode Support

Tailwind CSS provides built-in support for dark mode through the dark: variant to define both light and dark mode styles within the same element. This built-in functionality allows developers to easily implement dark mode without complex styling overrides, making it a valuable feature for modern applications.

For example, a button might require a default light mode style with background-color: white; color: black; and then an entirely separate rule for dark mode with background-color: black; color: white;. This approach results in larger stylesheets, increased specificity conflicts, and a more challenging debugging process. Instead of duplicating CSS properties, bg-white dark:bg-black can be used to achieve the same effect.

Furthermore, since Tailwind generates only the necessary styles, there’s no risk of shipping unused dark mode CSS, which often occurs in traditional approaches when developers define styles that are not always applied.

No Need for Separate CSS Files

With Tailwind CSS, styles are applied using utility classes directly within markup, eliminating the need for separate external CSS files. This means you don’t need to maintain large CSS files with thousands of lines of custom styles. Instead, all styling logic is contained within the HTML or JSX itself, leading to a more streamlined development process.

You no longer need to switch between multiple files to tweak a component’s appearance. Everything is conveniently contained within the component itself, streamlining collaboration and improving efficiency, particularly in larger projects or teams.

Additionally, Tailwind ensures that only the styles actually used in a project make it into the final CSS bundle, automatically removing all unused styles in production builds. This results in a significantly smaller CSS file compared to traditional approaches, where unused or redundant styles may still be included.

Extensible with Plugins

Tailwind CSS is highly extensible, allowing you to add new utilities and extend variants using plugins. While many customizations—such as adding new spacing values, colors, or responsive variants—can be achieved by extending the theme, plugins enable deeper modifications that go beyond the default capabilities.

For example, a plugin can be used to define new animation utilities. Plugins are particularly useful for standardizing project-specific design patterns or adding missing CSS features. This level of extensibility makes Tailwind CSS a good choice for projects that require advanced customizations while maintaining a consistent and scalable styling system.

Benefits of Tailwind CSS

Rapid Development

Tailwind CSS accelerates development by allowing developers to build interfaces without writing custom CSS. The utility-first approach eliminates the need to switch between HTML and CSS files, keeping all styling within the markup. This not only speeds up prototyping but also enhances efficiency in production environments. By using predefined classes such as p-4, text-center, and bg-blue-500, you can apply styles instantly without creating separate class definitions.

The time-saving aspect extends beyond styling, as teams can maintain uniformity in UI design without additional CSS maintenance. It also reduces onboarding friction for new developers, as they don’t need to learn a custom design system or search through large CSS files for appropriate styles. The simplicity and flexibility of Tailwind makes it ideal for startups and enterprises looking for rapid UI development.

Consistent Design

One of the biggest advantages of Tailwind CSS is its built-in design consistency. Traditional CSS often requires developers to create custom styles, which can lead to inconsistencies across different components. Tailwind solves this problem by enforcing a strict design system with predefined values for spacing, typography, colors, and breakpoints. By using utility classes such as text-lg, leading-tight, and tracking-wide, developers can maintain a uniform look across their entire application.

This approach minimizes design drift and ensures that UI elements remain visually consistent even as the project scales. Tailwind’s @theme directive also allows you to define custom design tokens, making it easy to create custom themes while maintaining a consistent visual identity.

Improved Maintainability

Maintaining CSS codebases can be challenging, especially in larger projects where styles become tangled over time. Tailwind simplifies maintenance by eliminating the need for custom stylesheets, reducing the risk of conflicting styles and specificity issues. Since styles are applied directly in the markup, developers can easily modify components without searching through multiple CSS files.

This inline styling approach also improves collaboration, as developers can instantly understand and modify styles without needing prior knowledge of the project’s CSS structure. Additionally, the reduced reliance on class name conventions removes the need for enforcing naming standards, leading to cleaner and more maintainable codebases.

Optimized for Performance

Performance optimization is a key benefit of Tailwind CSS. Unlike component libraries that include a large number of unused styles, Tailwind ensures that only the styles used in the project are included in the final CSS file. This results in smaller file sizes and faster page load times.

The reduced file size translates into better Core Web Vitals scores, directly improving metrics such as First Contentful Paint (FCP) and Largest Contentful Paint (LCP), which are critical for user experience and search engine optimization (SEO).

Highly Customizable

While Tailwind CSS provides an extensive set of utility classes by default, it also allows you to create custom styles to match specific design needs. You can modify the default configuration using the @theme directive to define custom colors, typography, spacing, and more. Instead of being limited to a predefined design language, you have full control over the look and feel of your project, while ensuring consistency.

Tailwind’s plugin ecosystem further enhances its flexibility, enabling you to extend its capabilities with additional utilities and variants. Whether working on a simple web page or a complex enterprise application, Tailwind adapts to your project’s needs without compromising maintainability.

Limitations of Tailwind CSS

Steep Learning Curve

Despite its many advantages, Tailwind CSS presents a steep learning curve for developers who are new to utility-first frameworks. Unlike vanilla CSS, which relies on semantic class names and separate stylesheets, Tailwind requires developers to work with a vast set of utility classes to style elements directly within the markup.

This shift in approach can feel overwhelming at first, particularly for those accustomed to component-based or BEM-style methodologies. The need to remember numerous class names and structure designs without conventional CSS rules may initially slow down development, making the transition challenging for those unfamiliar with utility-driven styling.

Large HTML Files

Since Tailwind applies styles directly within the markup, HTML files can quickly become cluttered with multiple utility classes. This can make the structure harder to read, especially in complex components with extensive styling requirements.

Unlike vanilla CSS, where styles are abstracted into separate stylesheets, Tailwind requires developers to manage styling inline, which some may find challenging for long-term maintainability.

To address this, Tailwind provides directives like @apply, which allows you to extract commonly used utility patterns into reusable CSS classes. By grouping frequently repeated styles into named classes, @apply helps maintain a cleaner and more structured codebase while preserving the benefits of Tailwind’s utility-first approach.

Lack of Semantic Class Names

Another common critique of Tailwind CSS is its lack of semantic class names. Traditional CSS encourages the use of descriptive class names that indicate an element's purpose, such as .navbar or .card-header, making it easier to understand the structure and intent of the code.

Tailwind’s utility classes focus on styling rather than functionality, describing an element’s appearance rather than its role. This can make it more challenging to infer an element’s purpose at a glance, especially for developers reviewing unfamiliar code.

Why Prefer Tailwind CSS?

Over Vanilla CSS

Prebuilt Classes: Tailwind comes with a comprehensive set of utility classes, reducing the need for repetitive custom CSS. Unlike vanilla CSS, where you write styles from scratch, Tailwind provides predefined classes for layout, spacing, typography, colors, and more. This helps to build UIs faster and maintain consistency across projects without managing multiple CSS files. By using utility classes such as p-4, text-lg, bg-blue-500, etc., you can apply styles directly in the markup.

No More Naming: Unlike vanilla CSS, where you create meaningful and unique class names to avoid conflicts, Tailwind eliminates this requirement by using utility classes. This reduces the overhead of naming conventions. With Tailwind, you can focus on styling rather than coming up with semantic class names. This makes it easier to maintain large projects and collaborate across teams without worrying about conflicting styles.

Rapid Prototyping: By using predefined utility classes, you can quickly build fully responsive and styled interfaces. This approach is particularly beneficial for prototyping, as changes can be made instantly without modifying multiple CSS files. You can experiment with different styles and layouts in real-time, speeding up the design and development process, in turn ensuring that projects move from concept to implementation efficiently.

Smaller CSS Bundle: Tailwind ensures that only the used styles are included in the final CSS bundle. Unlike vanilla CSS frameworks that come with a large, predefined stylesheet, Tailwind generates styles dynamically, removing unused classes and reducing the overall CSS file size. This results in improved performance, faster page loads, and a better user experience, particularly for those on slower connections.

No Specificity Issues: One of the biggest challenges in vanilla CSS is managing specificity conflicts, where styles unintentionally override each other due to complex cascading rules. Since all utilities in Tailwind CSS have the same specificity, conflicts are resolved based on the order of classes in the generated CSS, not by competing specificity rules. This eliminates the need for complex selector hierarchies or !important overrides, making your code cleaner, more maintainable, and easier to debug.

Built-in Responsiveness: Tailwind includes built-in responsive variants, allowing you to easily create layouts that adapt to different screen sizes. Instead of writing custom media queries, you can use prefixes like sm:, md:, lg:, and xl: to define styles for different breakpoints. This simplifies the development process and ensures that UIs remain flexible and adaptable across devices, from mobile phones to large desktop screens.

Over Component Libraries

Greater Customization: Tailwind doesn’t impose predefined styles or components. Instead, it provides utility classes to create completely custom designs without being restricted by an existing design system. This makes Tailwind ideal for projects that require unique branding and design flexibility. You can define custom color palettes, typography, and more in Tailwind, ensuring that every aspect of the UI aligns with the project’s design requirements.

Lightweight Output: Unlike component libraries that include a broad set of styles and components (many of which may never be used), Tailwind generates only the necessary styles at build time, resulting in a significantly smaller final CSS file. This enhances page performance by reducing load times and improving overall efficiency. Tailwind ensures that projects remain lightweight and optimized without unnecessary CSS bloat.

No Opinionated Components: Unlike libraries like MUI, which provide pre-styled components that may need to be overridden, Tailwind gives developers full control over their UI components. It does not dictate how buttons, forms, or cards should look, allowing for complete customization. Instead, it allows you to style components from scratch using the utility classes. This flexibility makes Tailwind an excellent choice for teams that want full creative control over their designs without being constrained by predefined styles.

Framework Agnostic: Tailwind CSS is inherently framework-agnostic, meaning it works seamlessly with any JavaScript framework or technology stack. Unlike component libraries that are tied to specific frameworks (e.g., MUI for React, Vuetify for Vue, or Svelte Material UI for Svelte), Tailwind’s utility classes are purely CSS-based and can be used in any project. Whether you're building a static HTML site, a React application, or a server-rendered project with frameworks like Next.js or Laravel, Tailwind integrates effortlessly without imposing framework-specific constraints.

What's New in Tailwind CSS v4?

FeatureTailwind v4Tailwind v3
Performance

Completely overhauled engine; ~3.5x faster full builds and up to 100x faster incremental builds.

Slower build times, requiring more manual configuration.

Configuration

Configure directly in CSS with @theme, removing the need for a separate tailwind.config.js file.

Relies on tailwind.config.js for all project-specific customization and settings.

Content Detection

Automatic detection with heuristics; respects .gitignore and no manual array needed.

Requires a content array in tailwind.config.js to specify which files to scan.

Import Support

Native @import support included; no extra PostCSS plugins needed.

Needed an additional plugin (e.g., postcss-import) for handling @import statements.

Color Palette

Modern P3 color palette using OKLCH, offering a broader gamut and more vibrant colors.

Relied on the standard sRGB color space, which is more limited in range.

Dynamic Utilities

Arbitrary values and custom utilities supported without extra configuration or arbitrary value syntax.

More limited dynamic usage; often required explicit configuration fallback or arbitrary value syntax.

Build Performance

Tailwind CSS v4.0 introduces a completely overhauled high-performance engine. This update rebuilds the framework from the ground up, incorporating all the insights gained over the years to maximize speed.

Source: Tailwind CSS
v3.4v4.0Improvement
Full build378ms100ms3.78x
Incremental rebuild with new CSS44ms5ms8.8x
Incremental rebuild with no new CSS35ms192µs182x

According to official benchmarks, full rebuilds are now over 3.5 times faster, while incremental builds can achieve speeds more than 8 times faster. The most notable gain occurs with incremental builds that don’t require compiling any new CSS — those are reportedly 100 times faster and finish in just microseconds.

Over extended development, these rapid builds become increasingly common because developers tend to reuse previously utilized classes such as flex, col-span-2, or font-bold.

Configuration

One significant update in Tailwind CSS v4.0 is the move from configuring projects with JavaScript to configuring them directly in CSS. Rather than using a tailwind.config.js file, all customizations can now be set within the CSS file where Tailwind is imported, eliminating the need for an extra configuration file.

@theme {
  colors: {
    primary: #1E3A8A;
    secondary: #9333EA;
    accent: #FACC15;
  };
}

This new CSS-first setup offers nearly the same capabilities as tailwind.config.js, allowing developers to manage design tokens, create custom utilities and variants, and perform other advanced configurations right in the CSS.

Content Detection

In Tailwind CSS v3, it was necessary to maintain a content array to specify which files should be scanned. With version 4.0, new heuristics handle detection automatically, removing the need for that manual configuration.

For example, anything listed in a .gitignore file is automatically excluded, preventing unnecessary scans of dependencies or generated files that are not tracked in version control, such as:

/node_modules
/coverage
/.next/
/build

All binary file types (images, videos, .zip files, etc.) are ignored by default as well. If a file or directory that is excluded by default needs to be included, the @source directive can be used directly in the CSS file:

@import "tailwindcss";
@source "../node_modules/@my-company/ui-lib";

This @source directive uses the same underlying heuristics, so it continues to skip binary file formats and anything else that should be excluded, without requiring explicit extension listings.

Import Support

Before Tailwind CSS v4.0, importing other CSS files inline using @import required configuring an additional plugin such as postcss-import. Version 4.0 now includes this functionality by default, removing the need for extra tools:

// postcss.config.js
export default {
  plugins: [
    "postcss-import",
    "@tailwindcss/postcss",
  ],
};

This import mechanism was developed specifically for Tailwind CSS and is integrated directly into its engine, delivering faster performance due to the tight integration.

Color Palette

Tailwind CSS v4.0 includes a modernized P3 color palette, transitioning from RGB to OKLCH to leverage a broader color gamut and produce more vibrant shades where the sRGB space was previously restrictive.

@theme {
  --color-red-50: oklch(0.971 0.013 17.38);
  --color-red-100: oklch(0.936 0.032 17.717);
  --color-red-200: oklch(0.885 0.062 18.334);
  --color-red-300: oklch(0.808 0.114 19.571);
  --color-red-400: oklch(0.704 0.191 22.216);
  --color-red-500: oklch(0.637 0.237 25.331);
  --color-red-600: oklch(0.577 0.245 27.325);
  --color-red-700: oklch(0.505 0.213 27.518);
  --color-red-800: oklch(0.444 0.177 26.899);
  --color-red-900: oklch(0.396 0.141 25.723);
  --color-red-950: oklch(0.258 0.092 26.042);
  /* ...more default colors*/

Although the entire palette has undergone a refresh, the relative balance among colors is preserved. As a result, even though everything has been updated, upgrading projects from the earlier version should not feel disruptive.

Dynamic Utilities

Tailwind CSS v4.0 simplifies many utilities and variants by allowing specific types of arbitrary values directly—no extra configuration or fallback to arbitrary value syntax is required. For example, it’s now possible to create grids of practically any dimension:

<div className="grid grid-cols-24">
  <!-- ... -->
</div>

Likewise, it’s easy to target custom boolean data attributes without defining them upfront:

<div data-active class="opacity-50 data-active:opacity-100">
  <!-- ... -->
</div>

Additionally, spacing utilities—like mt-*, ml-*, w-*, or p-*—are automatically derived from a single spacing variable, enabling them to handle any value by default:

@layer theme {
  :root {
    --spacing: 0.25rem;
  }
}

@layer utilities {
  .mt-12 {
    margin-top: calc(var(--unit-size) * 12);
  }
  .ml-20 {
    margin-left: calc(var(--unit-size) * 20);
  }
  .p-5 {
    padding: calc(var(--unit-size) * 5);
  }
}

Visit the official blog for the complete list of updates in Tailwind v4.

Get Started with Tailwind

Tailwind CDN

Tailwind CSS offers a CDN (Content Delivery Network) version, allowing developers to use the framework instantly without installation or setup. By including a single script tag in an HTML file, you can start using Tailwind’s utility classes right away. This is particularly useful for rapid prototyping, testing, or small projects that don’t require a build process.

To learn how to install Tailwind CDN in your project, visit Tailwind CDN installation guide.

Tailwind CLI

Tailwind CSS provides a CLI (Command Line Interface) tool that allows developers to generate and process Tailwind styles without requiring complex build tools like Webpack or Vite. It offers a lightweight, standalone way to compile Tailwind styles, making it ideal for both simple projects and automated workflows.

To learn how to install Tailwind CLI in your project, visit Tailwind CLI installation guide.

Real World Examples

A horizontal scrolling product card carousel with scroll padding for better product visibility.

This is a live editor. Play around with it!
export default function ProductCarousel() {
  const products = [
    {
      id: 1,
      name: "Wireless Headphones",
      price: "$299",
      image: "https://images.unsplash.com/photo-1546435770-a3e426bf472b",
      alt: "Wireless Headphones"
    },
    {
      id: 2,
      name: "Smart Watch",
      price: "$199",
      image: "https://images.unsplash.com/photo-1579586337278-3befd40fd17a",
      alt: "Smart Watch"
    },
    {
      id: 3,
      name: "Laptop",
      price: "$999",
      image: "https://images.unsplash.com/photo-1496181133206-80ce9b88a853",
      alt: "Laptop"
    },
    {
      id: 4,
      name: "Smartphone",
      price: "$799",
      image: "https://images.unsplash.com/photo-1533228100845-08145b01de14",
      alt: "Smartphone"
    },
    {
      id: 5,
      name: "Tablet",
      price: "$499",
      image: "https://images.unsplash.com/photo-1527698266440-12104e498b76",
      alt: "Tablet"
    },
    {
      id: 6,
      name: "Camera",
      price: "$699",
      image: "https://images.unsplash.com/photo-1502920917128-1aa500764cbd",
      alt: "Camera"
    }
  ];

  return (
    <div className="scroll-pl-6 scroll-pr-6 snap-x snap-mandatory overflow-x-auto flex gap-6 p-6">
      {products.map((product) => (
        <div
          key={product.id}
          className="snap-center shrink-0 w-72 bg-white rounded-xl shadow-lg"
        >
          <img
            src={product.image}
            alt={product.alt}
            className="w-full h-48 object-cover rounded-t-xl"
          />
          <div className="p-4">
            <h3 className="text-lg font-semibold">{product.name}</h3>
            <p className="text-blue-600 font-bold mt-2">{product.price}</p>
            <button className="w-full mt-4 bg-blue-500 text-white py-2 rounded-lg hover:bg-blue-600">
              Add to Cart
            </button>
          </div>
        </div>
      ))}
    </div>
  );
}

Nested Documentation Sidebar

A documentation sidebar menu with elegant hover states and nested submenus that expand to overlay subsequent content.

This is a live editor. Play around with it!
import React, { useState } from 'react';

export default function DocsSidebar() {
  const [hoveredItem, setHoveredItem] = useState(null);

  const sidebarData = [
    {
      id: 'getting-started',
      title: 'Getting Started',
      icon: '🚀',
      subItems: [
        { id: 'gs-1', title: 'Quick Start Guide' },
        { id: 'gs-2', title: 'Installation' },
        { id: 'gs-3', title: 'Basic Configuration' },
        { id: 'gs-4', title: 'Project Structure' }
      ]
    },
    {
      id: 'components',
      title: 'Components',
      icon: '🧩',
      subItems: [
        { id: 'comp-1', title: 'Button' },
        { id: 'comp-2', title: 'Input' },
        { id: 'comp-3', title: 'Card' },
        { id: 'comp-4', title: 'Modal' },
        { id: 'comp-5', title: 'Navigation' },
        { id: 'comp-6', title: 'Table' }
      ]
    },
    {
      id: 'hooks',
      title: 'Hooks',
      icon: '🎣',
      subItems: [
        { id: 'hook-1', title: 'useState' },
        { id: 'hook-2', title: 'useEffect' },
        { id: 'hook-3', title: 'useContext' },
        { id: 'hook-4', title: 'useRef' }
      ]
    },
    {
      id: 'styling',
      title: 'Styling',
      icon: '🎨',
      subItems: [
        { id: 'style-1', title: 'Theme Configuration' },
        { id: 'style-2', title: 'Custom Styles' },
        { id: 'style-3', title: 'Responsive Design' },
        { id: 'style-4', title: 'Dark Mode' }
      ]
    },
    {
      id: 'api',
      title: 'API Reference',
      icon: '📚',
      subItems: [
        { id: 'api-1', title: 'REST API' },
        { id: 'api-2', title: 'GraphQL' },
        { id: 'api-3', title: 'WebSocket' },
        { id: 'api-4', title: 'Authentication' },
        { id: 'api-5', title: 'Error Handling' }
      ]
    },
    {
      id: 'deployment',
      title: 'Deployment',
      icon: '🚢',
      subItems: [
        { id: 'deploy-1', title: 'Build Process' },
        { id: 'deploy-2', title: 'Environment Setup' },
        { id: 'deploy-3', title: 'CI/CD Pipeline' }
      ]
    }
  ];

  return (
    <div className="h-[450px] w-[450px] overflow-hidden bg-gray-50">
      <aside className="h-full w-64 overflow-y-auto border-r bg-white">
        <div className="p-4">
          <h2 className="mb-4 text-lg font-bold text-gray-800">Documentation</h2>
          <nav className="space-y-1">
            {sidebarData.map((section, index) => (
              <div
                key={section.id}
                className="relative"
                onMouseEnter={() => setHoveredItem(section.id)}
                onMouseLeave={() => setHoveredItem(null)}
              >
                <button
                  className={`flex w-full items-center rounded-md px-3 py-2 text-sm font-medium transition-colors
                    ${hoveredItem === section.id 
                      ? 'bg-blue-50 text-blue-700' 
                      : 'text-gray-700 hover:bg-gray-50 hover:text-gray-900'
                    }`}
                >
                  <span className="mr-2">{section.icon}</span>
                  {section.title}
                </button>

                {/* Submenu */}
                {hoveredItem === section.id && (
                  <div 
                    className="absolute left-0 z-50 mt-0 w-full rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5"
                    style={{
                      maxHeight: '200px',
                      overflow: 'auto'
                    }}
                  >
                    {section.subItems.map((item) => (
                      <a
                        key={item.id}
                        href="#"
                        className="block px-4 py-2 text-sm text-gray-700 hover:bg-blue-50 hover:text-blue-700"
                      >
                        {item.title}
                      </a>
                    ))}
                  </div>
                )}
              </div>
            ))}
          </nav>
        </div>
      </aside>
    </div>
  );
}

Travel Destination Cards

A set of travel destination cards with a scrollable background that remains fixed while content moves.

This is a live editor. Play around with it!
export default function TravelDestinations() {
  const destinations = [
    {
      id: 1,
      name: "Paris",
      country: "France",
      rating: 4.8,
      src: "https://images.unsplash.com/photo-1502602898657-3e91760cbb34",
      alt: "Paris cityscape"
    },
    {
      id: 2,
      name: "Tokyo",
      country: "Japan",
      rating: 4.9,
      src: "https://images.unsplash.com/photo-1503899036084-c55cdd92da26",
      alt: "Tokyo street view"
    },
    {
      id: 3,
      name: "New York",
      country: "USA",
      rating: 4.7,
      src: "https://images.unsplash.com/photo-1496442226666-8d4d0e62e6e9",
      alt: "New York skyline"
    },
    {
      id: 4,
      name: "Sydney",
      country: "Australia",
      rating: 4.6,
      src: "https://images.unsplash.com/photo-1506973035872-a4ec16b8e8d9",
      alt: "Sydney opera house"
    },
    {
      id: 5,
      name: "Dubai",
      country: "UAE",
      rating: 4.8,
      src: "https://images.unsplash.com/photo-1512453979798-5ea266f8880c",
      alt: "Dubai skyline"
    },
    {
      id: 6,
      name: "Rome",
      country: "Italy",
      rating: 4.7,
      src: "https://images.unsplash.com/photo-1525874684015-58379d421a52",
      alt: "Rome colosseum"
    }
  ];

  return (
    <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 p-6">
      {destinations.map((destination) => (
        <div key={destination.id} className="rounded-xl overflow-hidden shadow-lg h-96">
          <div
            className="h-full bg-fixed bg-center bg-cover relative"
            style={{ backgroundImage: `url(${destination.src})` }}
          >
            <div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black to-transparent p-6">
              <h3 className="text-2xl font-bold text-white">{destination.name}</h3>
              <p className="text-white mb-2">{destination.country}</p>
              <div className="flex items-center">
                <span className="text-yellow-400">★</span>
                <span className="text-white ml-1">{destination.rating}</span>
              </div>
            </div>
          </div>
        </div>
      ))}
    </div>
  );
}

Recipe Card Collection

A collection of recipe cards with varying backdrop saturation effects based on difficulty level.

This is a live editor. Play around with it!
// RecipeCollection.jsx

export default function RecipeCollection() {
  const recipes = [
    {
      id: 1,
      name: "Spicy Thai Curry",
      difficulty: "Hard",
      time: "45 mins",
      image: "https://images.unsplash.com/photo-1455619452474-d2be8b1e70cd",
      alt: "Thai curry dish",
      chef: "Chef Maria",
    },
    {
      id: 2,
      name: "Classic Caesar Salad",
      difficulty: "Easy",
      time: "15 mins",
      image: "https://images.unsplash.com/photo-1551248429-40975aa4de74",
      alt: "Caesar salad",
      chef: "Chef John",
    },
    {
      id: 3,
      name: "Chocolate Soufflé",
      difficulty: "Expert",
      time: "60 mins",
      image: "https://images.unsplash.com/photo-1606313564200-e75d5e30476c",
      alt: "Chocolate soufflé",
      chef: "Chef Pierre",
    },
    {
      id: 4,
      name: "Mushroom Risotto",
      difficulty: "Medium",
      time: "35 mins",
      image: "https://images.unsplash.com/photo-1476124369491-e7addf5db371",
      alt: "Mushroom risotto",
      chef: "Chef Isabella",
    },
    {
      id: 5,
      name: "Sushi Rolls",
      difficulty: "Hard",
      time: "50 mins",
      image: "https://images.unsplash.com/photo-1579871494447-9811cf80d66c",
      alt: "Sushi rolls",
      chef: "Chef Tanaka",
    },
    {
      id: 6,
      name: "Mediterranean Pasta",
      difficulty: "Medium",
      time: "25 mins",
      image: "https://images.unsplash.com/photo-1473093226795-af9932fe5856",
      alt: "Mediterranean pasta",
      chef: "Chef Marco",
    },
  ];

  // Dynamically generate backdrop-saturate classes for normal & hover states
  // e.g. Easy: normal saturate-50 → hover saturate-100, Hard: normal saturate-150 → hover saturate-200
  const getSaturationClass = (difficulty) => {
    switch (difficulty) {
      case "Easy":
        return "backdrop-saturate-50 group-hover:backdrop-saturate-100";
      case "Medium":
        return "backdrop-saturate-100 group-hover:backdrop-saturate-150";
      case "Hard":
        return "backdrop-saturate-150 group-hover:backdrop-saturate-200";
      case "Expert":
        return "backdrop-saturate-200 group-hover:backdrop-saturate-[3]";
      default:
        return "backdrop-saturate-100 group-hover:backdrop-saturate-150";
    }
  };

  return (
    <div className="p-8 bg-gray-100 min-h-screen">
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
        {recipes.map((recipe) => (
          <div
            key={recipe.id}
            // Card container with recipe image as background
            style={{ backgroundImage: `url(${recipe.image})` }}
            className="
              group
              relative
              bg-cover bg-center
              rounded-xl
              overflow-hidden
              shadow-lg
              h-64
              cursor-pointer
            "
          >
            {/* 
              Semi-transparent overlay with backdrop-filter.
              We use dynamic saturation classes + hover states. 
            */}
            <div
              className={`
                absolute
                inset-0
                z-10
                bg-black/40
                backdrop-filter
                transition-all
                duration-300
                ${getSaturationClass(recipe.difficulty)}
              `}
            />

            {/* 
              The text/content is above the overlay. 
              Using "relative z-20" ensures it's visible on top of the overlay.
            */}
            <div className="relative z-20 p-4 text-white drop-shadow-md">
              <h3 className="text-xl font-bold mb-2">{recipe.name}</h3>
              <div className="flex justify-between items-center mb-2">
                <span className="text-sm">{recipe.time}</span>
                <span
                  className={`
                    px-3 py-1
                    rounded-full
                    text-sm
                    ${
                      recipe.difficulty === "Easy"
                        ? "bg-green-100 text-green-800"
                        : recipe.difficulty === "Medium"
                        ? "bg-yellow-100 text-yellow-800"
                        : recipe.difficulty === "Hard"
                        ? "bg-orange-100 text-orange-800"
                        : "bg-red-100 text-red-800"
                    }
                   `}
                >
                  {recipe.difficulty}
                </span>
              </div>
              <p>By {recipe.chef}</p>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

Notification Cards

A set of notification cards with different border colors based on their status.

This is a live editor. Play around with it!
export default function NotificationList() {
  const notifications = [
    {
      id: 1,
      title: "System Update",
      message: "New features have been deployed",
      status: "info",
      time: "2 minutes ago"
    },
    {
      id: 2,
      title: "Payment Failed",
      message: "Unable to process payment",
      status: "error",
      time: "5 minutes ago"
    },
    {
      id: 3,
      title: "Order Shipped",
      message: "Your order #12345 has been shipped",
      status: "success",
      time: "10 minutes ago"
    },
    {
      id: 4,
      title: "Low Stock Alert",
      message: "Product XYZ is running low",
      status: "warning",
      time: "15 minutes ago"
    },
    {
      id: 5,
      title: "New Comment",
      message: "John commented on your post",
      status: "info",
      time: "20 minutes ago"
    },
    {
      id: 6,
      title: "Backup Complete",
      message: "System backup finished successfully",
      status: "success",
      time: "25 minutes ago"
    }
  ];

  const borderColors = {
    info: "border-blue-500",
    error: "border-red-500",
    success: "border-green-500",
    warning: "border-yellow-500"
  };

  return (
    <div className="space-y-4 p-6">
      {notifications.map((notification) => (
        <div
          key={notification.id}
          className={`p-4 border-l-4 bg-white shadow-sm ${
            borderColors[notification.status]
          }`}
        >
          <h3 className="font-bold">{notification.title}</h3>
          <p className="text-gray-600">{notification.message}</p>
          <span className="text-sm text-gray-500">{notification.time}</span>
        </div>
      ))}
    </div>
  );
}

Tailwind Learning Path

To become proficient in Tailwind CSS, follow three key steps: learning, practicing, and applying. Start with the official documentation, which explains the framework’s utility-first approach, configuration options, and available utilities.

Next, practice by experimenting with utility classes in a controlled environment like Tailwind Play. This helps you understand how different classes work, test layouts, and build small UI components without project constraints.

Finally, apply your skills by working on real-world projects where styling decisions impact usability and maintainability. Use platforms like Frontend Mentor, Dev Challenges, and Frontend Practice to build complete UIs and refine your approach to structuring Tailwind styles efficiently.