├── LICENSE.md ├── composer.json ├── config └── tailwind-merge.php └── src ├── Facades └── TailwindMerge.php ├── TailwindMergeServiceProvider.php └── helpers.php /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Sandro Gehri 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gehrisandro/tailwind-merge-laravel", 3 | "description": "TailwindMerge for Laravel merges multiple Tailwind CSS classes by automatically resolving conflicts between them", 4 | "keywords": [ 5 | "laravel", 6 | "php", 7 | "tailwindcss", 8 | "merge", 9 | "classes" 10 | ], 11 | "license": "MIT", 12 | "authors": [ 13 | { 14 | "name": "Sandro Gehri", 15 | "email": "sandrogehri@gmail.com" 16 | } 17 | ], 18 | "require": { 19 | "php": "^8.1.0", 20 | "guzzlehttp/guzzle": "^7.5.1", 21 | "laravel/framework": "^10.9.0|^11.0|^12.0", 22 | "gehrisandro/tailwind-merge-php": "^v1.1.0" 23 | }, 24 | "require-dev": { 25 | "laravel/pint": "^1.13.8", 26 | "orchestra/testbench": "^8.0|^9.0|^10.0", 27 | "pestphp/pest": "^v2.30.0|^3.7", 28 | "pestphp/pest-plugin-arch": "^2.6|^3.0", 29 | "pestphp/pest-plugin-type-coverage": "^2.8|^3.3", 30 | "phpstan/phpstan": "^1.10.55|^2.1", 31 | "rector/rector": "^0.19|^2.0", 32 | "symfony/var-dumper": "^6.4.2|^7.0" 33 | }, 34 | "autoload": { 35 | "psr-4": { 36 | "TailwindMerge\\Laravel\\": "src/" 37 | }, 38 | "files": [ 39 | "src/helpers.php" 40 | ] 41 | }, 42 | "autoload-dev": { 43 | "psr-4": { 44 | "Tests\\": "tests/" 45 | } 46 | }, 47 | "minimum-stability": "dev", 48 | "prefer-stable": true, 49 | "config": { 50 | "sort-packages": true, 51 | "preferred-install": "dist", 52 | "allow-plugins": { 53 | "pestphp/pest-plugin": true 54 | } 55 | }, 56 | "extra": { 57 | "laravel": { 58 | "providers": [ 59 | "TailwindMerge\\Laravel\\TailwindMergeServiceProvider" 60 | ] 61 | } 62 | }, 63 | "scripts": { 64 | "refactor:lint": "pint -v", 65 | "refactor:rector": "rector", 66 | "test:lint": "pint --test -v", 67 | "test:refactor": "rector --dry-run", 68 | "test:types": "phpstan analyse --ansi", 69 | "test:type-coverage": "pest --type-coverage --min=100", 70 | "test:pest": "pest --colors=always", 71 | "test": [ 72 | "@test:lint", 73 | "@test:refactor", 74 | "@test:types", 75 | "@test:type-coverage", 76 | "@test:pest" 77 | ] 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /config/tailwind-merge.php: -------------------------------------------------------------------------------- 1 | env('TAILWIND_MERGE_PREFIX', null), 15 | 16 | /* 17 | |-------------------------------------------------------------------------- 18 | | Class groups 19 | |-------------------------------------------------------------------------- 20 | | 21 | | If TailwindMerge is not able to merge your changes properly you can 22 | | modify the merge process by modifying existing class groups or adding 23 | | new class groups. 24 | | 25 | | For example, if you want to add a custom font size of 'very-large': 26 | | 'classGroups' => [ 27 | | 'font-size' => [ 28 | | ['text' => ['very-large']] 29 | | ], 30 | | ], 31 | */ 32 | 33 | 'classGroups' => [], 34 | 35 | /* 36 | |-------------------------------------------------------------------------- 37 | | Blade directive 38 | |-------------------------------------------------------------------------- 39 | | 40 | | Here you may specify the name of the blade directive that will be used. 41 | | Set it to null to entirely disable the blade directive. 42 | */ 43 | 44 | 'blade_directive' => 'twMerge', 45 | ]; 46 | -------------------------------------------------------------------------------- /src/Facades/TailwindMerge.php: -------------------------------------------------------------------------------- 1 | app->singleton(TailwindMergeContract::class, static fn (): TailwindMerge => TailwindMerge::factory() 18 | ->withConfiguration(config('tailwind-merge', [])) 19 | ->withCache(app('cache')->store()) // @phpstan-ignore-line 20 | ->make()); 21 | 22 | $this->app->alias(TailwindMergeContract::class, 'tailwind-merge'); 23 | $this->app->alias(TailwindMergeContract::class, TailwindMerge::class); 24 | } 25 | 26 | public function boot(): void 27 | { 28 | if ($this->app->runningInConsole()) { 29 | $this->publishes([ 30 | __DIR__ . '/../config/tailwind-merge.php' => config_path('tailwind-merge.php'), 31 | ]); 32 | } 33 | 34 | $this->registerBladeDirectives(); 35 | $this->registerAttributesBagMacros(); 36 | } 37 | 38 | protected function registerBladeDirectives(): void 39 | { 40 | $this->app->afterResolving('blade.compiler', function (BladeCompiler $bladeCompiler): void { 41 | $name = config('tailwind-merge.blade_directive', 'twMerge'); 42 | 43 | if ($name === null) { 44 | return; 45 | } 46 | 47 | $bladeCompiler->directive($name, fn (?string $expression): string => ""); 48 | }); 49 | } 50 | 51 | protected function registerAttributesBagMacros(): void 52 | { 53 | ComponentAttributeBag::macro('twMerge', function (...$args): ComponentAttributeBag { 54 | /** @var ComponentAttributeBag $this */ 55 | $this->offsetSet('class', resolve(TailwindMergeContract::class)->merge($args, ($this->get('class', '')))); 56 | 57 | return $this; 58 | }); 59 | 60 | ComponentAttributeBag::macro('twMergeFor', function (string $for, ...$args): ComponentAttributeBag { 61 | /** @var ComponentAttributeBag $this */ 62 | 63 | /** @var TailwindMergeContract $instance */ 64 | $instance = resolve(TailwindMergeContract::class); 65 | 66 | $attribute = 'class' . ($for !== '' ? ':' . $for : ''); 67 | 68 | /** @var string $classes */ 69 | $classes = $this->get($attribute, ''); 70 | 71 | $this->offsetSet('class', $instance->merge($args, $classes)); 72 | 73 | return $this->only('class'); 74 | }); 75 | 76 | ComponentAttributeBag::macro('withoutTwMergeClasses', function (): ComponentAttributeBag { 77 | /** @var ComponentAttributeBag $this */ 78 | return $this->whereDoesntStartWith('class:'); 79 | }); 80 | } 81 | 82 | /** 83 | * @return array>|string[] 84 | */ 85 | public function provides(): array 86 | { 87 | return [ 88 | TailwindMerge::class, 89 | TailwindMergeContract::class, 90 | 'tailwind-merge', 91 | ]; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | > ...$args 8 | */ 9 | function twMerge(...$args): string 10 | { 11 | return app('tailwind-merge')->merge(...$args); 12 | } 13 | } 14 | --------------------------------------------------------------------------------