How it started ...
A couple of days ago, I shared the creation of Movie Vibes, an AI-powered Spring Boot application that analyzes movie "vibes" using Spring AI and Ollama. The backend was working beautifully, but it was time to build a proper user interface. What started as a simple "add React + Tailwind" task turned into an educational journey through modern frontend development challenges, framework limitations, and the beauty of getting back to fundamentals.
How it's going ...
The Original Plan: React + Tailwind CSS
The plan seemed straightforward:
- ✅ React 18 + TypeScript for the frontend
- ✅ Tailwind CSS for rapid styling
- ✅ Modern, responsive design
- ✅ Quick development cycle
How hard could it be? Famous last words.
The Tailwind CSS Nightmare
The Promise vs. Reality
Tailwind CSS markets itself as a "utility-first CSS framework" that accelerates development. In theory, you get:
- Rapid prototyping with utility classes
- Consistent design tokens
- Smaller CSS bundles
- No context switching between CSS and HTML
In practice, with Create React App and Tailwind v4, we got:
- ๐ซ Build failures due to PostCSS plugin incompatibilities
- ๐ซ Cryptic error messages about plugin configurations
- ๐ซ Hours of debugging CRACO configurations
- ๐ซ Version conflicts between Tailwind v4 and CRA's PostCSS setup
The Technical Issues
We tried multiple solutions:
- CRACO configuration - Failed with plugin conflicts
- Downgrading to Tailwind v3 - Still had PostCSS issues
- Custom PostCSS config - Broke Create React App's build process
- Ejecting CRA - Nuclear option, but defeats the purpose
The Breaking Point
After spending more time debugging Tailwind than actually building features, I made a decision: dump Tailwind entirely. Sometimes the best solution is the simplest one.
The Pure CSS Renaissance
Going Back to Fundamentals
Instead of fighting with framework abstractions, we built a custom CSS design system that:
- ✅ Compiles instantly - No build step complications
- ✅ Full control - Every pixel exactly where we want it
- ✅ No dependencies - Zero external CSS frameworks
- ✅ Better performance - Only the CSS we actually use
- ✅ Maintainable - Clear, semantic class names
The CSS Architecture
/* Semantic, maintainable class names */
.movie-card {
background: white;
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.3s ease;
}
.movie-card:hover {
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
}
/* Responsive design without utility class bloat */
@media (max-width: 768px)
{
.movie-card {
/* Mobile-specific styles */
}
}
Compare this to Tailwind's approach:
<!-- Tailwind: Utility class soup -->
<div className="bg-white rounded-xl shadow-lg p-6 hover:shadow-2xl
transition-shadow duration-300 md:p-8 lg:p-10">
The AI Timeout Challenge
The Problem
Once the UI was working, we discovered a new issue: AI operations take time. Our local Ollama model could take 30-60 seconds to analyze a movie and generate recommendations. The frontend was timing out before the AI finished processing.
The Solution
We implemented a comprehensive timeout strategy:
// 2-minute timeout for AI operations
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 120000);
// User-friendly loading messages
<p className="loading-text">
Please wait, this process can take 30-60 seconds while our AI agent
analyzes the movie and generates recommendations ✨
</p>
- ⏱️ Extended timeout to 2 minutes for AI operations
- ๐ฏ Clear user expectations with realistic time estimates
- ๐ Graceful error handling with timeout-specific messages
- ๐ฑ Loading states that don't feel broken
The Poster Image Quest
Backend Enhancement
The original backend only returned movie titles in recommendations. Users expect to see poster images! We enhanced the system to:
- Fetch complete metadata for the main movie ✅
- Parse AI-generated recommendations to extract movie titles
- Query OMDb API for each recommendation's metadata
- Include poster URLs in the API response
Performance Optimization
To balance richness with performance:
- ๐ฏ Limit to 5 recommendations to avoid excessive API calls
- ๐ก️ Fallback handling when movie metadata isn't found
- ๐ Detailed logging for debugging and monitoring
The Final Architecture
Frontend Stack
- React 18 + TypeScript - Modern, type-safe development
- Pure CSS - Custom utility system, no framework dependencies
- Responsive Design - Mobile-first approach
- Error Boundaries - Graceful handling of failures
Backend Enhancements
- Spring Boot 3.x - Robust, production-ready API
- Spring AI + Ollama - Local LLM for movie analysis
- OMDb API Integration - Rich movie metadata
- Intelligent Caching - Future enhancement opportunity
API Evolution
{
"movie": {
"title": "Mission: Impossible",
"poster": "https://...",
"year": "1996",
"imdbRating": "7.2",
"plot": "Full plot description..."
},
"vibeAnalysis": "An exhilarating action-adventure...",
"recommendations": [
{
"title": "The Bourne Identity",
"poster": "https://...",
"year": "2002",
"imdbRating": "7.9"
}
]
}
Lessons Learned
1. Framework Complexity vs. Value
Tailwind's Promise: | Rapid development with utility classes |
---|---|
Reality: |
Build system complexity that outweighs benefits |
Sometimes vanilla CSS is the better choice. Modern CSS is incredibly powerful:
- CSS Grid and Flexbox for layouts
- CSS Custom Properties for theming
- CSS Container Queries for responsive design
- CSS-in-JS when you need dynamic styles
2. AI UX Considerations
Building AI-powered applications requires different UX patterns:
- ⏳ Longer wait times are normal and expected
- ๐ข Clear communication about processing time
- ๐ Progressive disclosure of results
- ๐ก️ Robust error handling for AI failures
3. API Design Evolution
Starting simple and evolving based on frontend needs:
- ๐ฏ Backend-driven initially (simple JSON responses)
- ๐จ Frontend-driven enhancement (rich metadata)
- ๐ Backward compatibility during transitions
4. The Beauty of Fundamentals
Modern development often pushes us toward complex abstractions, but sometimes the simplest solution is the best:
- Pure CSS over CSS frameworks
- Semantic HTML over div soup
- Progressive enhancement over JavaScript-heavy approaches
Performance Results
After our optimizations:
- ๐ Build time: 3 seconds (was 45+ seconds with Tailwind debugging)
- ๐ฆ Bundle size: 15% smaller without Tailwind dependencies
- ⚡ Development experience: Hot reload works consistently
- ๐ฏ User experience: Clear loading states, beautiful poster images
What's Next?
The Movie Vibes application is now production-ready with:
- ✅ Beautiful, responsive UI
- ✅ AI-powered movie analysis
- ✅ Rich movie metadata with posters
- ✅ Robust error handling
- ✅ 2-minute AI operation support
Future enhancements could include:
- ๐️ Caching layer for popular movies
- ๐ฅ User accounts and favorites
- ๐ Dark mode theme
- ๐ณ Docker deployment setup
- ๐งช Comprehensive testing suite
Conclusion: Embrace Simplicity
This journey reinforced a fundamental principle: complexity should solve real problems, not create them.
Tailwind CSS promised to accelerate our development but instead became a roadblock. Pure CSS, with its directness and simplicity, delivered exactly what we needed without the framework overhead.
Building AI-powered applications comes with unique challenges - long processing times, complex data transformations, and user experience considerations that traditional web apps don't face. Focus on solving these real problems rather than fighting your tools.
- Backend: mvn spring-boot:run
- Frontend: npm start
- Search for your favorite movie and discover its vibe! ๐ฌ✨
What's your experience with CSS frameworks? Have you found cases where vanilla CSS outperformed framework solutions? Share your thoughts in the comments!
Tech Stack:
- Spring Boot 3.x + Spring AI
- React 18 + TypeScript
- Pure CSS (Custom Design System)
- Ollama (Local LLM)
- OMDb API
GitHub: tyrell/movievibes