Skip to content
Learni
View all tutorials
Développement Frontend

How to Master Advanced Tailwind CSS in 2026

Lire en français

Introduction

Tailwind CSS has revolutionized frontend development in 2026 with its mature JIT (Just-In-Time) mode, extensible plugins, and arbitrary values for pixel-perfect precision. This advanced tutorial is for senior developers ready to go beyond the basics: creating dynamic themes, building custom plugins, optimizing production builds, and adding smooth animations without CSS bloat. Think of Tailwind as 'CSS Lego' where every brick is a utility—here, we'll craft our own bricks.

Why it matters: Modern apps demand ultra-performant, themeable (dark/light), and scalable UIs. With 95% bundle size reduction via purging, Tailwind shines in production. This guide delivers 100% functional code for a responsive dashboard, ready to copy-paste into Vite or Next.js. Time: 20 minutes for a pro prototype.

Prerequisites

  • Node.js 20+ and npm/yarn/pnpm
  • Solid knowledge of HTML/CSS/JS and PostCSS
  • Vite or Next.js installed (we'll use Vite here)
  • Familiarity with Tailwind basics (utility classes, responsive)

Setting up the Vite + Tailwind project

terminal
npm create vite@latest tailwind-advanced -- --template vanilla
cd tailwind-advanced
npm install
tailwindcss postcss autoprefixer -D
npx tailwindcss init -p
npm install

This script creates a vanilla Vite project, installs Tailwind with PostCSS, and generates the tailwind.config.js and postcss.config.js configs. Vite provides fast HMR for iterating on Tailwind styles without full rebuilds.

Basic Tailwind Configuration

Now update the config files to scan your templates. In tailwind.config.js, add content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}']. In src/style.css, import the directives @tailwind base; @tailwind components; @tailwind utilities;. Test with npm run dev: a blank page with active Tailwind confirms the setup.

Custom theme with design tokens

tailwind.config.js
export default {
  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
  darkMode: 'class',
  theme: {
    extend: {
      colors: {
        primary: {
          50: '#eff6ff',
          500: '#3b82f6',
          900: '#1e3a8a',
        },
        neutral: {
          100: '#f5f5f5',
          800: '#1f2937',
        },
      },
      fontFamily: {
        sans: ['Inter', 'ui-sans-serif'],
      },
      spacing: {
        '18': '4.5rem',
        '128': '32rem',
      },
      animation: {
        'fade-in': 'fadeIn 0.5s ease-in-out',
      },
      keyframes: {
        fadeIn: {
          '0%': { opacity: '0', transform: 'translateY(10px)' },
          '100%': { opacity: '1', transform: 'translateY(0)' },
        },
      },
    },
  },
  plugins: [],
};

This config extends the theme with semantic colors (primary/neutral), custom spacing, and keyframe animations. darkMode: 'class' enables dark/light toggling via the dark: prefix. Design tokens scale your designs without CSS duplication.

Using the theme in a responsive UI

Apply these tokens in a dashboard. Use grid-cols-, gap- for fluid layouts, and hover:, focus: for interactions. Prefixes like md:, lg: handle responsive design like smart media queries.

Responsive dashboard with animations

index.html
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
  <meta charset="UTF-8" />
  <link rel="icon" type="image/svg+xml" href="/vite.svg" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Advanced Tailwind</title>
  <link rel="stylesheet" href="/src/style.css" />
</head>
<body class="bg-neutral-100 dark:bg-neutral-800 min-h-screen font-sans antialiased">
  <div class="container mx-auto px-6 py-8 max-w-7xl">
    <header class="mb-12">
      <h1 class="text-4xl font-bold text-primary-900 dark:text-primary-100 animate-fade-in">Pro Dashboard</h1>
    </header>
    <main class="grid grid-cols-1 lg:grid-cols-4 gap-6">
      <div class="lg:col-span-3 bg-white dark:bg-neutral-900 p-8 rounded-2xl shadow-xl animate-fade-in">
        <h2 class="text-2xl font-semibold mb-6 text-neutral-800 dark:text-neutral-100">Chart</h2>
        <div class="w-full h-64 bg-gradient-to-r from-primary-500 to-primary-900 rounded-xl flex items-center justify-center text-white font-bold">Chart Content (e.g., Chart.js)</div>
      </div>
      <aside class="space-y-4">
        <div class="bg-white dark:bg-neutral-900 p-6 rounded-2xl shadow-xl hover:shadow-2xl transition-all duration-300 animate-fade-in">
          <h3 class="font-semibold mb-3 text-neutral-800 dark:text-neutral-100">Stats</h3>
          <div class="space-y-2">
            <p class="text-sm text-neutral-600 dark:text-neutral-400">Sales: <span class="font-bold text-primary-500">12k</span></p>
            <p class="text-sm text-neutral-600 dark:text-neutral-400">Users: <span class="font-bold text-primary-500">5k</span></p>
          </div>
        </div>
        <div class="bg-gradient-to-br from-primary-500 to-primary-900 p-6 rounded-2xl text-white hover:from-primary-600 transition-all duration-300 animate-fade-in">
          <button class="w-full py-3 px-4 bg-white/20 backdrop-blur-sm rounded-xl font-semibold hover:bg-white/30 transition-all">CTA Action</button>
        </div>
      </aside>
    </main>
  </div>
  <script type="module" src="/src/main.js"></script>
</body>
</html>

This dashboard uses grid-cols-* for an adaptive layout (1 col on mobile, 4 on desktop), dark/light themes, custom animations, and hover effects. Classes like backdrop-blur-sm and gradients leverage Tailwind extensions for pro glassmorphism rendering.

Custom plugin for reusable components

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

module.exports = plugin(function({ addComponents, theme }) {
  const cards = {
    '.card': {
      backgroundColor: 'white',
      '@dark': {
        backgroundColor: theme('colors.neutral.900'),
      },
      padding: theme('spacing.8'),
      borderRadius: theme('borderRadius.2xl'),
      boxShadow: theme('boxShadow.xl'),
      transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
      '&:hover': {
        boxShadow: theme('boxShadow.2xl'),
      },
    },
    '.btn-primary': {
      backgroundColor: theme('colors.primary.500'),
      color: 'white',
      padding: `${theme('spacing.3')} ${theme('spacing.4')}`,
      borderRadius: theme('borderRadius.xl'),
      fontWeight: '600',
      transition: 'all 0.2s ease',
      '&:hover': {
        backgroundColor: theme('colors.primary.600'),
      },
    },
  };

  addComponents(cards);
});

This plugin adds reusable .card and .btn-primary components with dark and hover variants. It uses theme() for consistency with your tokens, avoiding repetitive Tailwind classes in HTML.

Integrating the plugin and arbitrary values

Add the plugin to tailwind.config.js: plugins: [require('./tailwind-plugin')],.

Arbitrary values like w-[17.5rem] or bg-[radial-gradient(...)] enable precise styles without config. Example: shadow-[0_35px_60px_-15px_rgba(0,0,0,0.3)] for custom shadows.

Example with plugin and arbitrary values

index.html
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Tailwind Plugin</title>
  <link rel="stylesheet" href="/src/style.css" />
</head>
<body class="bg-gradient-to-br from-neutral-100 to-neutral-200 dark:from-neutral-900 dark:to-neutral-800 min-h-screen p-8">
  <div class="max-w-4xl mx-auto space-y-8">
    <div class="card p-0 overflow-hidden">
      <div class="bg-primary-500 text-white p-6">
        <h2 class="text-2xl font-bold">Card with Plugin</h2>
      </div>
      <div class="p-8">
        <p class="text-neutral-700 dark:text-neutral-300 mb-4">Smooth content with advanced hover shadow.</p>
        <button class="btn-primary w-full md:w-auto">Click me</button>
      </div>
    </div>
    <div class="card shadow-[0_20px_40px_-10px_rgba(0,0,0,0.1)] hover:shadow-[0_25px_50px_-15px_rgba(0,0,0,0.15)] bg-[conic-gradient(at_top,_var(--tw-gradient-stops))] from-primary-400 via-blue-500 to-purple-600 text-white p-8 rounded-3xl">
      <h3 class="text-xl font-semibold mb-2">Arbitrary Magic</h3>
      <p>Custom shadow and gradient without config!</p>
    </div>
  </div>
</body>
</html>

Here, the plugin's .card and .btn-primary simplify markup. Arbitrary shadow-[...] and bg-[conic-gradient(...)] add pro effects. Test the hover: cubic-bezier transitions for native smoothness.

Build optimization with purging and JIT

vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  css: {
    postcss: {
      plugins: [
        require('tailwindcss'),
        require('autoprefixer'),
      ],
    },
  },
  build: {
    rollupOptions: {
      output: {
        manualChunks: undefined,
      },
    },
  },
});

This Vite config enables PostCSS for Tailwind JIT and automatic purging (only used CSS generated). In production, bundles stay under 10kb. Avoid manualChunks to keep Tailwind monolithic and optimized.

Dynamic dark mode toggle

src/main.js
document.addEventListener('DOMContentLoaded', () => {
  const toggle = document.createElement('button');
  toggle.className = 'fixed top-4 right-4 p-3 bg-neutral-200 dark:bg-neutral-700 rounded-full shadow-lg hover:scale-110 transition-all z-50';
  toggle.innerHTML = '🌙';
  toggle.onclick = () => {
    document.documentElement.classList.toggle('dark');
    toggle.innerHTML = document.documentElement.classList.contains('dark') ? '☀️' : '🌙';
  };
  document.body.appendChild(toggle);

  // Persist in localStorage
  if (localStorage.theme === 'dark' || (!localStorage.theme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
    document.documentElement.classList.add('dark');
    toggle.innerHTML = '☀️';
  } else {
    document.documentElement.classList.remove('dark');
  }

  localStorage.theme = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
});

This script adds a persistent dark mode toggle (localStorage + prefers-color-scheme). Dynamic Tailwind classes via classList.toggle('dark'). Fixed positioning and scale-110 hover for premium UX.

Best Practices

  • Use @apply sparingly: Only for components (e.g., .btn { @apply px-6 py-3 font-semibold rounded-lg; }), not everywhere.
  • Aggressive purging: content: ['./**/*.{html,js}'] to eliminate 90% unused CSS.
  • Plugins over arbitrary: Build plugins for reusability, save arbitrary for one-offs.
  • Scalable themes: Define tokens in extend for overrides without crushing defaults.
  • A/B test: Use Tailwind Play for prototyping before production.

Common Mistakes to Avoid

  • Forgetting purging: Generates 3MB+ CSS; always set content paths.
  • Abusive nesting: Tailwind isn't Sass; limit @apply to 1 level for readability.
  • Static dark mode: Prefer class over media for user control.
  • No HMR: Without PostCSS/Vite, slow rebuilds; always use a modern bundler.

Next Steps

Master Headless UI + Tailwind for accessible components, or Tailwind Merge for conditional classNames in React/Vue.

Check out our Learni Dev trainings on Tailwind + React/Next.js. Official docs: Tailwind CSS. Advanced examples on Tailwind Play.