Goswami Digital WorldBlog & Insights
All PostsMain SiteContact Us
โ† All PostsยทDesign

10 Tailwind CSS Tips That Will Change How You Style

Practical Tailwind CSS tips to write cleaner, faster, and more maintainable styles.

A
Ankur Goswami
10 February 2026 ยท 9 min read
๐Ÿ‘views|
โค๏ธlikes|
๐Ÿ”—shares
#tailwindcss#css#frontend

Why Tailwind?

Tailwind CSS has completely changed how modern developers write styles. Instead of jumping between HTML and separate CSS files, you write utility classes directly in your markup โ€” and your design stays right where your structure lives. No more naming classes, no more specificity wars, no more dead CSS piling up over time.

But Tailwind isn't just a shorthand for CSS. When you understand its deeper features โ€” arbitrary values, variants, plugins, JIT mode โ€” you unlock a styling workflow that's hard to go back from. Here are 10 tips that will genuinely level up the way you use it.


1. Use @apply Wisely (But Don't Overuse It)

@apply lets you extract repeated utility combinations into a single reusable class. It's perfect for things like buttons, badges, and form inputs that appear many times across your app.

/* globals.css */
.btn-primary {
  @apply px-6 py-3 bg-blue-600 text-white rounded-lg font-semibold
         hover:bg-blue-700 active:scale-95 transition-all duration-200;
}

.badge {
  @apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
}

.badge-green {
  @apply badge bg-green-100 text-green-800;
}
<!-- Clean usage in HTML -->
<button class="btn-primary">Get Started</button>
<span class="badge-green">Active</span>

When to use it: Only for patterns that repeat 5+ times across your project. Don't @apply everything โ€” that defeats the purpose of utility-first CSS and kills tree-shaking benefits.

When NOT to use it: One-off components, layouts, page-specific styles. Just write the utilities directly in the class attribute.

Pro tip: If you're using React or Vue, prefer a cn() utility (like clsx + tailwind-merge) over @apply for component variants. It's more flexible and keeps your styles colocated with your logic.


2. Arbitrary Values โ€” Escape the Grid When You Need To

Tailwind's design system covers 95% of cases, but sometimes a designer hands you a specific value that doesn't map to any preset. Arbitrary values let you use any CSS value while staying in Tailwind syntax.

<!-- Arbitrary sizing -->
<div class="w-[340px] h-[72px]">

<!-- Arbitrary colors (hex, hsl, rgb all work) -->
<div class="bg-[#1a1a2e] text-[#e0e0ff]">

<!-- Arbitrary spacing -->
<div class="mt-[72px] mb-[3.5rem]">

<!-- Arbitrary font sizes -->
<p class="text-[15px] leading-[1.8]">

<!-- Works with variants too -->
<div class="hover:bg-[#2d2d4e] dark:bg-[#0f0f23]">

You can even use CSS variables as arbitrary values:

<div class="bg-[var(--brand-color)] text-[var(--text-primary)]">

Real-world use case: When building a landing page with a brand color like #FF6B35 that isn't in Tailwind's palette, use bg-[#FF6B35] instead of writing a custom CSS class or extending the config.


3. Group and Peer Modifiers โ€” Style Children Based on Parent State

This is one of Tailwind's most underused features and it solves a classic CSS problem elegantly.

group โ€” Style child based on parent hover/state

<div class="group relative rounded-xl border border-gray-200 p-6 hover:border-blue-500 hover:shadow-lg transition-all">
  <h3 class="text-lg font-semibold text-gray-900 group-hover:text-blue-600 transition-colors">
    Card Title
  </h3>
  <p class="mt-2 text-gray-500 group-hover:text-gray-700 transition-colors">
    Card description goes here.
  </p>
  <svg class="absolute top-4 right-4 text-gray-300 group-hover:text-blue-400 transition-colors" .../>
</div>

peer โ€” Style a sibling based on another element's state

<label class="flex items-center gap-3 cursor-pointer">
  <input type="checkbox" class="peer sr-only" />
  <div class="w-10 h-6 rounded-full bg-gray-300 peer-checked:bg-blue-600 transition-colors relative">
    <div class="absolute left-1 top-1 w-4 h-4 bg-white rounded-full peer-checked:translate-x-4 transition-transform"></div>
  </div>
  <span class="text-sm text-gray-700 peer-checked:text-blue-600 font-medium">Enable notifications</span>
</label>

Named groups โ€” for nested parent/child relationships

<div class="group/card hover:shadow-xl">
  <div class="group/item flex items-center">
    <span class="group-hover/card:text-blue-600 group-hover/item:underline">Nested states</span>
  </div>
</div>

4. Responsive Design Without Media Query Clutter

Tailwind's mobile-first breakpoints (sm, md, lg, xl, 2xl) let you write responsive layouts entirely in class attributes.

<!-- Mobile-first layout -->
<div class="
  grid
  grid-cols-1
  sm:grid-cols-2
  lg:grid-cols-3
  xl:grid-cols-4
  gap-4
  sm:gap-6
">
  <!-- Cards -->
</div>

<!-- Typography that scales with screen -->
<h1 class="text-2xl sm:text-4xl lg:text-6xl font-bold leading-tight">
  Build faster with Tailwind
</h1>

<!-- Hide/show elements at breakpoints -->
<nav class="hidden lg:flex gap-8 items-center">
  <!-- Desktop nav -->
</nav>
<button class="lg:hidden">
  <!-- Mobile hamburger icon -->
</button>

Custom breakpoints in tailwind.config.js:

module.exports = {
  theme: {
    screens: {
      xs: '475px',     // custom extra small
      sm: '640px',
      md: '768px',
      lg: '1024px',
      xl: '1280px',
      '2xl': '1536px',
      '3xl': '1920px', // custom for large monitors
    },
  },
}

5. Dark Mode โ€” The Right Way

Tailwind supports dark mode via the dark: prefix. By default it uses the prefers-color-scheme media query, but you can also control it manually with the class strategy.

Setup in tailwind.config.js:

module.exports = {
  darkMode: 'class', // 'media' or 'class'
  // ...
}

Using dark variants:

<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 transition-colors duration-300">
  <h1 class="text-2xl font-bold text-blue-600 dark:text-blue-400">Hello World</h1>
  <p class="text-gray-600 dark:text-gray-400">This adapts to dark mode automatically.</p>
  <button class="bg-blue-600 dark:bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-700 dark:hover:bg-blue-400">
    Click me
  </button>
</div>

Toggle dark mode with JS:

// Add to your theme toggle button
const toggleTheme = () => {
  document.documentElement.classList.toggle('dark')
  localStorage.setItem(
    'theme',
    document.documentElement.classList.contains('dark') ? 'dark' : 'light'
  )
}

// Apply saved preference on page load
if (localStorage.theme === 'dark' || (!localStorage.theme && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
  document.documentElement.classList.add('dark')
}

6. Custom Animations and Keyframes

Tailwind comes with animate-spin, animate-ping, animate-bounce, and animate-pulse out of the box โ€” but you can add your own custom keyframe animations through the config.

In tailwind.config.js:

module.exports = {
  theme: {
    extend: {
      keyframes: {
        fadeInUp: {
          '0%': { opacity: '0', transform: 'translateY(20px)' },
          '100%': { opacity: '1', transform: 'translateY(0)' },
        },
        shimmer: {
          '0%': { backgroundPosition: '-200% 0' },
          '100%': { backgroundPosition: '200% 0' },
        },
        wiggle: {
          '0%, 100%': { transform: 'rotate(-3deg)' },
          '50%': { transform: 'rotate(3deg)' },
        },
      },
      animation: {
        'fade-in-up': 'fadeInUp 0.5s ease-out forwards',
        shimmer: 'shimmer 2s linear infinite',
        wiggle: 'wiggle 0.5s ease-in-out infinite',
      },
    },
  },
}

Usage:

<div class="animate-fade-in-up opacity-0">
  This fades in on load
</div>

<!-- Skeleton loader with shimmer -->
<div class="h-4 rounded bg-gray-200 animate-shimmer bg-gradient-to-r from-gray-200 via-gray-100 to-gray-200 bg-[length:200%_100%]">
</div>

<!-- Attention-grabbing icon -->
<button class="hover:animate-wiggle">
  ๐Ÿ””
</button>

7. clsx + tailwind-merge for Dynamic Classes in React

This is the go-to pattern for any React/Next.js project. Tailwind classes can conflict โ€” for example, p-4 and p-8 both set padding, but only one should win based on your logic.

Install:

npm install clsx tailwind-merge

Create a cn() utility:

// lib/utils.ts
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

Usage in components:

// Button component with variants
type ButtonProps = {
  variant?: 'primary' | 'secondary' | 'danger'
  size?: 'sm' | 'md' | 'lg'
  className?: string
  children: React.ReactNode
}

export function Button({ variant = 'primary', size = 'md', className, children }: ButtonProps) {
  return (
    <button
      className={cn(
        // Base styles
        'inline-flex items-center justify-center font-semibold rounded-lg transition-all duration-200',
        // Size variants
        {
          'px-3 py-1.5 text-sm': size === 'sm',
          'px-4 py-2 text-base': size === 'md',
          'px-6 py-3 text-lg': size === 'lg',
        },
        // Color variants
        {
          'bg-blue-600 text-white hover:bg-blue-700 active:scale-95': variant === 'primary',
          'bg-gray-100 text-gray-800 hover:bg-gray-200 active:scale-95': variant === 'secondary',
          'bg-red-600 text-white hover:bg-red-700 active:scale-95': variant === 'danger',
        },
        // Allow override from outside
        className
      )}
    >
      {children}
    </button>
  )
}
// Usage
<Button variant="primary" size="lg">Get Started</Button>
<Button variant="danger" className="w-full">Delete Account</Button>

8. Tailwind Plugins for Real-World Needs

Tailwind's official plugins add commonly-needed features without any custom CSS.

Install:

npm install @tailwindcss/typography @tailwindcss/forms @tailwindcss/aspect-ratio @tailwindcss/line-clamp

Configure:

// tailwind.config.js
module.exports = {
  plugins: [
    require('@tailwindcss/typography'),
    require('@tailwindcss/forms'),
    require('@tailwindcss/aspect-ratio'),
  ],
}

@tailwindcss/typography โ€” Beautiful prose

Perfect for blog posts, docs, and any content-heavy pages.

<article class="prose prose-lg prose-blue max-w-3xl mx-auto dark:prose-invert">
  <!-- Your Markdown-rendered HTML goes here -->
  <!-- All headings, paragraphs, code blocks, tables auto-styled -->
</article>

@tailwindcss/forms โ€” Consistent form resets

<input type="text" class="form-input w-full rounded-lg border-gray-300 focus:border-blue-500 focus:ring-blue-500" />
<select class="form-select rounded-lg border-gray-300">...</select>
<input type="checkbox" class="form-checkbox h-4 w-4 text-blue-600 rounded" />

@tailwindcss/aspect-ratio โ€” Responsive media

<div class="aspect-w-16 aspect-h-9">
  <iframe src="https://www.youtube.com/embed/..." allowfullscreen></iframe>
</div>

9. Extend the Theme Instead of Overriding It

A common mistake is overwriting Tailwind's default colors or spacing. Always extend so you keep the defaults.

// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        brand: {
          50:  '#eff6ff',
          100: '#dbeafe',
          200: '#bfdbfe',
          300: '#93c5fd',
          400: '#60a5fa',
          500: '#3b82f6',  // main brand color
          600: '#2563eb',
          700: '#1d4ed8',
          800: '#1e40af',
          900: '#1e3a8a',
        },
        surface: {
          DEFAULT: '#ffffff',
          secondary: '#f8fafc',
          tertiary: '#f1f5f9',
        }
      },
      fontFamily: {
        sans: ['Inter', 'system-ui', 'sans-serif'],
        mono: ['JetBrains Mono', 'Fira Code', 'monospace'],
        display: ['Cal Sans', 'Inter', 'sans-serif'],
      },
      spacing: {
        '18': '4.5rem',
        '88': '22rem',
        '128': '32rem',
      },
      borderRadius: {
        '4xl': '2rem',
      },
      boxShadow: {
        'soft': '0 2px 15px -3px rgba(0, 0, 0, 0.07), 0 10px 20px -2px rgba(0, 0, 0, 0.04)',
        'card': '0 1px 3px rgba(0,0,0,0.05), 0 20px 25px -5px rgba(0,0,0,0.08)',
      },
    },
  },
}

Usage:

<div class="bg-surface-secondary text-brand-700 shadow-soft rounded-4xl p-18">
  Custom design system values in Tailwind syntax
</div>

10. Optimize for Production โ€” Purge Unused CSS

In production, Tailwind scans your source files and removes every unused class. The result is a CSS file that's often just 5โ€“15 KB even for large projects.

tailwind.config.js โ€” content paths:

module.exports = {
  content: [
    './src/**/*.{js,ts,jsx,tsx,mdx}',
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
    './app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  // ...
}

Important: Don't dynamically construct class names

// โŒ Bad โ€” Tailwind can't detect these at build time
const color = 'blue'
<div className={`bg-${color}-500`}>  // bg-blue-500 will be purged

// โœ… Good โ€” Full class name present in source
const classes = {
  blue: 'bg-blue-500',
  red: 'bg-red-500',
  green: 'bg-green-500',
}
<div className={classes[color]}>

Safelist classes that are dynamic:

module.exports = {
  safelist: [
    'bg-blue-500',
    'bg-red-500',
    'bg-green-500',
    {
      pattern: /bg-(red|green|blue)-(100|500|900)/,
    },
  ],
}

Check your final bundle size:

npx tailwindcss -i ./src/input.css -o ./dist/output.css --minify
# Should be < 15KB for most projects

Conclusion

Tailwind CSS isn't just "CSS without writing CSS" โ€” it's a complete design system toolkit. The more you lean into its features โ€” group/peer modifiers, arbitrary values, cn() utilities, plugins, and custom theme extensions โ€” the more you realize that it can handle almost any UI requirement without reaching for custom stylesheets.

The biggest unlock is treating Tailwind's config as your design system's source of truth. Define your brand colors, typography scale, spacing, and shadows once in the config, and then everything in your app stays consistent automatically.

Start applying these 10 tips in your next project and you'll write cleaner, faster, and more maintainable styles โ€” guaranteed.

Enjoyed this post?
...
Share this post
๐Ÿ’ฌ๐•inF
โ† Back to BlogWork With Us โ†’