Tailwind CSS transforms Laravel frontend development. Utility-first approach eliminates custom CSS while maintaining design flexibility. At ZIRA Software, Tailwind accelerates UI development across Laravel projects.
Installation
Install Tailwind:
npm install tailwindcss@latest
npx tailwindcss init
Install PostCSS plugins:
npm install postcss autoprefixer
Laravel Mix Configuration
webpack.mix.js:
const mix = require('laravel-mix');
mix.js('resources/js/app.js', 'public/js')
.postCss('resources/css/app.css', 'public/css', [
require('tailwindcss'),
require('autoprefixer'),
]);
if (mix.inProduction()) {
mix.version();
}
Tailwind Configuration
tailwind.config.js:
module.exports = {
purge: [
'./resources/**/*.blade.php',
'./resources/**/*.js',
'./resources/**/*.vue',
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
},
},
fontFamily: {
sans: ['Inter', 'sans-serif'],
mono: ['Fira Code', 'monospace'],
},
spacing: {
'72': '18rem',
'84': '21rem',
'96': '24rem',
},
},
},
variants: {
extend: {
backgroundColor: ['active'],
opacity: ['disabled'],
},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
],
}
CSS Setup
resources/css/app.css:
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Custom components */
@layer components {
.btn {
@apply px-4 py-2 rounded font-semibold transition;
}
.btn-primary {
@apply bg-primary-600 text-white hover:bg-primary-700;
}
.btn-secondary {
@apply bg-gray-600 text-white hover:bg-gray-700;
}
.card {
@apply bg-white rounded-lg shadow-md p-6;
}
.input {
@apply block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500;
}
}
/* Custom utilities */
@layer utilities {
.text-shadow {
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}
}
Blade Layout
resources/views/layouts/app.blade.php:
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name') }}</title>
<link href="{{ mix('css/app.css') }}" rel="stylesheet">
<script src="{{ mix('js/app.js') }}" defer></script>
</head>
<body class="bg-gray-100">
<nav class="bg-white shadow-lg">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex items-center">
<a href="/" class="text-xl font-bold text-gray-900">
{{ config('app.name') }}
</a>
</div>
<div class="flex items-center space-x-4">
@auth
<span class="text-gray-700">{{ Auth::user()->name }}</span>
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit" class="btn btn-secondary">
Logout
</button>
</form>
@else
<a href="{{ route('login') }}" class="btn btn-secondary">
Login
</a>
@endauth
</div>
</div>
</div>
</nav>
<main class="py-8">
@yield('content')
</main>
</body>
</html>
Component Examples
Form example:
<div class="max-w-md mx-auto">
<div class="card">
<h2 class="text-2xl font-bold mb-6">Login</h2>
<form method="POST" action="{{ route('login') }}">
@csrf
<div class="mb-4">
<label for="email" class="block text-sm font-medium text-gray-700 mb-2">
Email
</label>
<input
type="email"
id="email"
name="email"
class="input @error('email') border-red-500 @enderror"
value="{{ old('email') }}"
required
>
@error('email')
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<div class="mb-6">
<label for="password" class="block text-sm font-medium text-gray-700 mb-2">
Password
</label>
<input
type="password"
id="password"
name="password"
class="input @error('password') border-red-500 @enderror"
required
>
@error('password')
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<button type="submit" class="w-full btn btn-primary">
Login
</button>
</form>
</div>
</div>
Card grid:
<div class="max-w-7xl mx-auto px-4">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach($posts as $post)
<div class="card hover:shadow-xl transition">
@if($post->image)
<img src="{{ $post->image }}" alt="{{ $post->title }}" class="w-full h-48 object-cover rounded-t-lg -mt-6 -mx-6 mb-4">
@endif
<h3 class="text-xl font-bold mb-2">{{ $post->title }}</h3>
<p class="text-gray-600 mb-4">{{ Str::limit($post->excerpt, 100) }}</p>
<div class="flex items-center justify-between">
<span class="text-sm text-gray-500">
{{ $post->created_at->diffForHumans() }}
</span>
<a href="{{ route('posts.show', $post) }}" class="btn btn-primary">
Read More
</a>
</div>
</div>
@endforeach
</div>
</div>
Responsive Design
Mobile-first approach:
<div class="container mx-auto px-4">
<!-- Mobile: stacked, Tablet: 2 cols, Desktop: 4 cols -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<!-- Content -->
</div>
<!-- Hide on mobile, show on tablet+ -->
<div class="hidden md:block">
<p>Visible on tablet and desktop</p>
</div>
<!-- Show on mobile only -->
<div class="md:hidden">
<p>Mobile menu</p>
</div>
<!-- Responsive text sizes -->
<h1 class="text-2xl md:text-4xl lg:text-6xl font-bold">
Responsive Heading
</h1>
</div>
Production Build
PurgeCSS configuration:
Already configured in tailwind.config.js purge option.
Build for production:
npm run production
Results:
- Development: ~783KB (all utilities)
- Production: ~10-30KB (only used utilities)
Dark Mode
Enable dark mode:
// tailwind.config.js
module.exports = {
darkMode: 'class', // or 'media'
// ...
}
Usage:
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
<p>Adapts to dark mode</p>
</div>
Toggle dark mode:
// resources/js/app.js
document.getElementById('darkModeToggle').addEventListener('click', () => {
document.documentElement.classList.toggle('dark');
localStorage.theme = document.documentElement.classList.contains('dark') ? 'dark' : 'light';
});
// On page load
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
}
Plugins
Install official plugins:
npm install @tailwindcss/forms @tailwindcss/typography @tailwindcss/aspect-ratio
Forms plugin:
<!-- Automatically styled forms -->
<input type="text" class="form-input">
<select class="form-select">
<option>Option 1</option>
</select>
<textarea class="form-textarea"></textarea>
Typography plugin:
<article class="prose lg:prose-xl">
{!! $post->content !!}
</article>
Conclusion
Tailwind CSS with Laravel Mix provides powerful, production-ready frontend workflow. Utility-first approach accelerates development while PurgeCSS ensures tiny production builds.
Need frontend development help? Contact ZIRA Software for Tailwind + Laravel expertise.