Chapter 4: Advanced Features

Haiyue
13min

Chapter 4: Advanced Features

Learning Objectives
  • Master the usage and best practices of the @apply directive
  • Learn to create custom utilities and extract components
  • Understand JIT mode and on-demand generation mechanism
  • Master new features and improvements in Tailwind CSS 4.1

@apply Directive

Basic Usage

The @apply directive allows you to apply Tailwind utilities in custom CSS:

/* styles.css */
.btn-primary {
  @apply bg-blue-500 text-white font-semibold py-2 px-4 rounded-lg;
  @apply hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500;
  @apply transition-colors duration-200;
}

.card {
  @apply bg-white rounded-lg shadow-md p-6;
}

.form-input {
  @apply block w-full px-3 py-2 border border-gray-300 rounded-md;
  @apply focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500;
}
<!-- Using custom component classes -->
<button class="btn-primary">Primary Button</button>
<div class="card">Card content</div>
<input type="text" class="form-input" placeholder="Enter...">

Best Practices

/* Recommended: Component-level extraction */
.btn {
  @apply font-semibold py-2 px-4 rounded-lg transition-colors duration-200;
  @apply focus:outline-none focus:ring-2 focus:ring-offset-2;
}

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

.btn-secondary {
  @apply btn bg-gray-200 text-gray-900;
  @apply hover:bg-gray-300 focus:ring-gray-500;
}

/* Avoid: Over-extracting basic utilities */
.text-large {
  @apply text-lg; /* Not recommended, using text-lg directly is better */
}

Conditional Application

/* Using @apply for complex states */
.nav-link {
  @apply block px-3 py-2 text-gray-700 font-medium;
  @apply hover:text-blue-600 hover:bg-blue-50;
  @apply transition-all duration-200;
}

.nav-link.active {
  @apply text-blue-600 bg-blue-50 border-r-2 border-blue-600;
}

/* Responsive @apply */
.responsive-card {
  @apply p-4 bg-white rounded-lg shadow;
}

@screen md {
  .responsive-card {
    @apply p-6 shadow-lg;
  }
}

Custom Utilities

Adding Custom Utilities

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      spacing: {
        '18': '4.5rem',
        '88': '22rem',
      },
      fontSize: {
        'xxs': '0.625rem',
      },
      colors: {
        'brand': {
          50: '#eff6ff',
          500: '#3b82f6',
          900: '#1e3a8a',
        }
      }
    }
  },
  plugins: [
    // Add custom utility plugin
    function({ addUtilities }) {
      const newUtilities = {
        '.text-shadow': {
          textShadow: '2px 2px 4px rgba(0,0,0,0.1)',
        },
        '.text-shadow-lg': {
          textShadow: '4px 4px 8px rgba(0,0,0,0.2)',
        },
        '.backface-hidden': {
          'backface-visibility': 'hidden',
        },
        '.perspective': {
          perspective: '1000px',
        }
      }

      addUtilities(newUtilities, ['responsive', 'hover'])
    }
  ]
}

Adding Components with Plugins

// tailwind.config.js
module.exports = {
  plugins: [
    function({ addComponents, theme }) {
      const components = {
        '.btn': {
          padding: `${theme('spacing.2')} ${theme('spacing.4')}`,
          borderRadius: theme('borderRadius.md'),
          fontWeight: theme('fontWeight.semibold'),
          transition: 'all 0.2s',
          '&:focus': {
            outline: 'none',
            boxShadow: `0 0 0 2px ${theme('colors.blue.500')}`,
          }
        },
        '.btn-primary': {
          backgroundColor: theme('colors.blue.500'),
          color: theme('colors.white'),
          '&:hover': {
            backgroundColor: theme('colors.blue.600'),
          }
        },
        '.btn-secondary': {
          backgroundColor: theme('colors.gray.200'),
          color: theme('colors.gray.900'),
          '&:hover': {
            backgroundColor: theme('colors.gray.300'),
          }
        }
      }

      addComponents(components)
    }
  ]
}

Custom Variants

// tailwind.config.js
module.exports = {
  plugins: [
    function({ addVariant }) {
      // Add custom pseudo-class variants
      addVariant('not-first', '&:not(:first-child)')
      addVariant('not-last', '&:not(:last-child)')
      addVariant('hocus', ['&:hover', '&:focus'])

      // Add data attribute variants
      addVariant('data-active', '&[data-active="true"]')
      addVariant('data-loading', '&[data-loading="true"]')
    }
  ]
}
<!-- Using custom variants -->
<div class="not-first:mt-4 not-last:mb-4">
  <button class="hocus:bg-blue-600 data-active:bg-green-500" data-active="true">
    Button
  </button>
</div>

JIT Mode Deep Dive

What is JIT Mode

JIT (Just-In-Time) mode is Tailwind CSS’s on-demand compilation mode that scans your code in real-time and generates only the CSS that’s actually used.

JIT Mode Advantages:

  • Smaller files during development
  • Support for arbitrary values
  • Faster build speeds
  • Better hot-reload experience

Enabling JIT Mode

// tailwind.config.js
module.exports = {
  mode: 'jit', // Enabled by default in Tailwind CSS 3.0+
  purge: [
    './src/**/*.{html,js,ts,jsx,tsx,vue}',
    './public/index.html',
  ],
  // Other configuration...
}

Arbitrary Value Support

JIT mode supports arbitrary values in square brackets:

<!-- Arbitrary color values -->
<div class="bg-[#1da1f2]">Twitter blue</div>
<div class="text-[rgb(255,0,0)]">Red text</div>

<!-- Arbitrary size values -->
<div class="w-[300px]">Fixed width 300px</div>
<div class="h-[50vh]">50% viewport height</div>
<div class="top-[117px]">Precise positioning</div>

<!-- Arbitrary spacing values -->
<div class="p-[1.7rem]">Custom padding</div>
<div class="m-[10px]">10px margin</div>

<!-- CSS variables -->
<div class="bg-[var(--main-color)]">Using CSS variable</div>

<!-- Complex values -->
<div class="bg-[url('/api/avatar')]">Dynamic background image</div>
<div class="before:content-['★']">Pseudo-element content</div>

Dynamic Class Name Generation

<!-- Dynamic colors -->
<div class="bg-[{{ userColor }}]">User-defined color</div>

<!-- Dynamic sizes -->
<div class="w-[{{ width }}px]">Dynamic width</div>

<!-- Responsive arbitrary values -->
<div class="lg:w-[calc(100%-2rem)]">Calculated width</div>

JIT Mode Considerations

JIT Mode Considerations
  1. String Concatenation: Avoid dynamically generating class name strings
  2. Whitelist: Ensure content configuration correctly scans all files
  3. Cache: May need to restart during development to apply configuration changes
  4. Arbitrary Value Syntax: Must use correct CSS syntax
// Not recommended: Dynamic string concatenation
const color = 'blue'
className = `bg-${color}-500` // JIT may not detect this

// Recommended: Complete class names or safelist
className = 'bg-blue-500'
// Or add to safelist in tailwind.config.js

Tailwind CSS 4.1 New Features

Improved TypeScript Support

// Type-safe configuration
import type { Config } from 'tailwindcss'

const config: Config = {
  content: [
    './src/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {
      colors: {
        primary: {
          50: '#eff6ff',
          500: '#3b82f6',
          950: '#172554',
        }
      }
    },
  },
  plugins: [],
}

export default config

New Utilities

<!-- New container query support -->
<div class="@container">
  <div class="@lg:text-xl">Container query responsive</div>
</div>

<!-- Improved grid utilities -->
<div class="grid grid-cols-subgrid">
  <div class="col-span-2">Subgrid support</div>
</div>

<!-- New logical properties -->
<div class="ms-4 me-4">Logical margins (RTL support)</div>
<div class="ps-4 pe-4">Logical padding</div>

Performance Improvements

// New optimization options
module.exports = {
  future: {
    hoverOnlyWhenSupported: true, // Only enable hover on devices that support it
  },
  experimental: {
    optimizeUniversalDefaults: true, // Optimize defaults
  }
}

New Plugin API

// Using new plugin API
const plugin = require('tailwindcss/plugin')

module.exports = {
  plugins: [
    plugin(function({ addUtilities, matchUtilities, theme }) {
      // New matchUtilities supports dynamic values
      matchUtilities(
        {
          'grid-area': (value) => ({
            'grid-area': value,
          }),
        },
        { values: theme('gridArea') }
      )
    }),
  ],
}

Practical Applications

Building a Design System

/* components.css */
@layer components {
  /* Button component system */
  .btn {
    @apply inline-flex items-center justify-center font-medium transition-all duration-200;
    @apply focus:outline-none focus:ring-2 focus:ring-offset-2;
    @apply disabled:opacity-50 disabled:cursor-not-allowed;
  }

  .btn-sm {
    @apply text-sm px-3 py-1.5 rounded-md;
  }

  .btn-md {
    @apply text-base px-4 py-2 rounded-lg;
  }

  .btn-lg {
    @apply text-lg px-6 py-3 rounded-xl;
  }

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

  .btn-outline {
    @apply btn bg-transparent border-2;
    @apply text-blue-600 border-blue-600;
    @apply hover:bg-blue-600 hover:text-white;
    @apply focus:ring-blue-500;
  }

  /* Form components */
  .form-group {
    @apply space-y-2;
  }

  .form-label {
    @apply block text-sm font-medium text-gray-700;
  }

  .form-input {
    @apply block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm;
    @apply focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500;
    @apply disabled:bg-gray-50 disabled:text-gray-500;
  }

  .form-error {
    @apply text-sm text-red-600;
  }

  /* Card components */
  .card {
    @apply bg-white rounded-lg border border-gray-200 shadow-sm;
  }

  .card-header {
    @apply px-6 py-4 border-b border-gray-200;
  }

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

  .card-footer {
    @apply px-6 py-4 bg-gray-50 border-t border-gray-200 rounded-b-lg;
  }
}

Using the Design System

<!-- Form example -->
<form class="max-w-md mx-auto space-y-6">
  <div class="form-group">
    <label for="email" class="form-label">Email Address</label>
    <input
      type="email"
      id="email"
      class="form-input"
      placeholder="Enter email"
    >
    <p class="form-error hidden">Please enter a valid email address</p>
  </div>

  <div class="form-group">
    <label for="password" class="form-label">Password</label>
    <input
      type="password"
      id="password"
      class="form-input"
      placeholder="Enter password"
    >
  </div>

  <div class="flex space-x-4">
    <button type="submit" class="btn btn-primary btn-md flex-1">
      Login
    </button>
    <button type="button" class="btn btn-outline btn-md">
      Register
    </button>
  </div>
</form>

<!-- Card example -->
<div class="card max-w-md mx-auto">
  <div class="card-header">
    <h3 class="text-lg font-semibold text-gray-900">User Information</h3>
  </div>
  <div class="card-body">
    <p class="text-gray-600">This is the main content area of the card...</p>
  </div>
  <div class="card-footer">
    <div class="flex justify-end space-x-3">
      <button class="btn btn-outline btn-sm">Cancel</button>
      <button class="btn btn-primary btn-sm">Confirm</button>
    </div>
  </div>
</div>

Summary

Through this chapter, you should have mastered:

  • @apply Directive: Reusing Tailwind utilities in custom CSS
  • Custom Utilities: Extending Tailwind’s functionality for specific needs
  • JIT Mode: On-demand compilation and arbitrary value support
  • Design System: Building a consistent component library with Tailwind
  • New Features: Improvements and optimizations in Tailwind CSS 4.1

These advanced features enable you to better organize code, improve development efficiency, and build maintainable design systems.