gehrisandro/tailwind-merge-php
Merge Tailwind CSS class strings in PHP with automatic conflict resolution (last class wins), ported from tailwind-merge. Supports Tailwind v3.0–v3.4, configurable and cacheable. Requires PHP 8.1+.
Install the package via Composer:
composer require gehrisandro/tailwind-merge-php
First Use Case: Merge conflicting Tailwind classes in a Blade template or Livewire component:
use TailwindMerge\TailwindMerge;
// In a Blade view or component method
$mergedClasses = TailwindMerge::instance()->merge('text-red-500', 'text-blue-500');
// Output: 'text-blue-500'
Where to Look First:
Create a custom Blade directive to merge classes globally:
// In AppServiceProvider@boot()
Blade::directive('merge', function ($expression) {
return "<?php echo TailwindMerge::instance()->merge({$expression}); ?>";
});
// Usage in Blade:
<div class="@merge(['bg-red-500', 'bg-blue-500'])">Merged!</div>
Merge classes dynamically in Livewire properties:
use Livewire\Component;
use TailwindMerge\TailwindMerge;
class ButtonComponent extends Component
{
public $baseClasses = 'px-4 py-2 rounded';
public $variant = 'primary'; // 'primary', 'secondary'
public function getMergedClassesProperty()
{
return TailwindMerge::instance()->merge(
$this->baseClasses,
$this->variant === 'primary'
? 'bg-blue-500 hover:bg-blue-600 text-white'
: 'bg-gray-500 hover:bg-gray-600 text-gray-800'
);
}
public function render()
{
return view('livewire.button', [
'classes' => $this->mergedClassesProperty,
]);
}
}
Merge classes based on runtime data (e.g., user roles, tenant themes):
use TailwindMerge\TailwindMerge;
class EmailService
{
public function generateButtonClasses(string $role): string
{
$base = 'px-4 py-2 rounded font-medium';
$roleClasses = match ($role) {
'admin' => 'bg-red-500 text-white',
'user' => 'bg-blue-500 text-white',
default => 'bg-gray-500 text-gray-800',
};
return TailwindMerge::instance()->merge($base, $roleClasses);
}
}
Merge error/state classes dynamically:
use Illuminate\Validation\Validator;
use TailwindMerge\TailwindMerge;
Validator::extend('custom-classes', function ($attribute, $value, $parameters, $validator) {
$merged = TailwindMerge::instance()->merge(
'border border-gray-300',
$validator->errors()->has($attribute) ? 'border-red-500' : 'border-green-500'
);
// Use $merged in your form logic...
});
Apply merging to all views via a composer:
// In AppServiceProvider@boot()
View::composer('*', function ($view) {
$view->with('mergedClasses', function () {
return TailwindMerge::instance()->merge(
'text-gray-800 dark:text-gray-200',
request()->user()?->prefersDarkMode() ? 'bg-gray-900' : 'bg-white'
);
});
});
// In Blade:
<body class="{{ $mergedClasses }}">
Assert merged classes in PHPUnit:
use TailwindMerge\TailwindMerge;
use Tests\TestCase;
class TailwindMergeTest extends TestCase
{
public function testMergeConflicts()
{
$merged = TailwindMerge::instance()->merge('p-4 px-6', 'p-8');
$this->assertEquals('px-6', $merged);
}
public function testDarkMode()
{
$merged = TailwindMerge::instance()->merge(
'text-black',
'dark:text-white dark:text-gray-700'
);
$this->assertEquals('text-black dark:text-gray-700', $merged);
}
}
Custom Tailwind Config Mismatches
tailwind.config.js uses non-standard class names (e.g., custom colors like bg-custom-red), you must update the classGroups configuration:
TailwindMerge::factory()->withConfiguration([
'classGroups' => [
'colors' => [
['bg' => ['custom-red']],
['text' => ['custom-red']],
],
],
])->make();
tailwind.config.js with the original package’s config docs.Arbitrary Values ([...]) Overrides
bg-[var(--color)]) always win over standard Tailwind classes. If this isn’t desired, avoid mixing them or use the !important modifier (!bg-red-500).TailwindMerge::instance()->merge('bg-[var(--color)]', '!bg-red-500');
// Result: '!bg-red-500' (if !important is supported in your build)
Caching Stale Configurations
classGroups config, clear the cache:
php artisan cache:clear
Non-Tailwind Classes
custom-class) are preserved but may cause unexpected behavior if they conflict with Tailwind’s internal logic. Test thoroughly.PHP 8.1+ Requirement
php -v
Performance with Large Class Lists
Dark Mode and State Conflicts
dark:...) are merged last, so later classes override earlier ones. Example:
TailwindMerge::instance()->merge('text-white', 'dark:text-black');
// Result: 'text-white dark:text-black' (not 'dark:text-black')
!important if needed.Inspect Raw Merging Logic
$tw = TailwindMerge::factory()->withDebug(true)->make();
$merged = $tw->merge('p-4 px-6');
// Check $tw->getDebugLog() for details.
Validate Tailwind Config
npx tailwindcss -i input.css -o output.css --content ./resources/views/**/*.blade.php
Test Edge Cases
lg:p-4 md:p-6).!important and arbitrary values (!text-red-500 bg-[var(--color)]).group-hover:text-red-500 hover:text-blue-500).Fallback for Unsupported Classes
$tw = TailwindMerge::factory()->withConfiguration([
'classGroups' => [
'custom' => [
['your-prefix' => ['your-class']],
],
],
])->make();
use TailwindMerge\Validators\ArbitraryValidator;
class CustomArbitraryValidator extends ArbitraryValidator
{
protected function getSupportedLabels(): array
{
return array_merge(parent::getSupportedLabels
How can I help you explore Laravel packages today?