Can we do width transition using TailwindCSS?

Postmortem: Unanimated Progress Bar Transition in TailwindCSS

Summary

During progress bar implementation using TailwindCSS, setting transition-duration concurrently with width changes prevented smooth animation. The duration-3000 CSS class was applied simultaneously with the 100% width update, causing the transition effect to be bypassed.

Root Cause

  • CSS transitions require persistent timing configuration during state changes
  • transition-duration: 0s was active when width values changed, disabling interpolation
  • Transition timing was changed at the exact moment as the width update (progress === 100)
  • Browsers cancel animations when timing properties change mid-transition

Why This Happens in Real Systems

  1. Parallel state updates: Progress values and transition parameters are frequently updated together
  2. Atomic UI changes: Teams bundle visual state updates in single renders
  3. Incremental progress patterns: Missing requirement for persistent transition timing
  4. Tooling assumptions: Over-reliance on Tailwind’s utility classes without CSS fundamentals

Real-World Impact

  • Progress bars snap to 100% without animation
  • Users perceive completed actions as unresponsive
  • Reduced perception of successful operations
  • Degraded UX for loading states and progress tracking

Example Code

jsx
// Problematic implementation
<div
className={transition-all ${progress === 100 ? 'duration-3000' : 'duration-0'}}
style={{ width: ${progress}% }}
/>

// Fixed implementation 🔧
<div
className=”transition-all duration-3000″ // Persistent timing
style={{ width: ${progress}% }}
/>

How Senior Engineers Fix It

  1. Decouple timing from state: Apply consistent transition-duration
  2. Stabilize timing parameters: Remove conditional duration classes
  3. Transition purity: Animate only transform/opacity properties when possible
  4. Alternative approach: Use CSS keyframes for complex progress animations
    jsx
    // Using Tailwind animation

// tailwind.config.js
theme: {
extend: {
animation: {
progress: ‘progress 3s linear’
},
keyframes: {
progress: {
‘0%’: { width: ‘0%’ },
‘100%’: { width: ‘100%’ }
}
}
}
}

Why Juniors Miss It

  1. Tailwind abstraction: Overlook CSS fundamentals behind utility classes
  2. Timing misconception: Assume transition duration applies to future changes
  3. React state model: Expect batched updates to trigger animations automatically
  4. Browser rendering gap: Undervalue persistent CSS properties during DOM updates
    jsx
    // Common junior pattern ❌
    useEffect(() => {
    if (success) {
    setWidth(100) // Expect animation to start here
    setDuration(3000) // But duration changes simultaneously
    }
    }, [success])