dxgx/blade-tailwind-extract
Composer 安装命令:
composer require dxgx/blade-tailwind-extract
包简介
Extract Tailwind CSS classes to reduce Livewire wire transfer size
README 文档
README
A Laravel package that dramatically reduces Livewire component wire transfer sizes by extracting long Tailwind CSS class strings into short, reusable CSS class names. Perfect for applications with large lists of Livewire components.
The Problem
When using Livewire with Tailwind CSS, long class strings in Blade templates get transferred over the wire on every component update:
<div class="flex flex-col gap-2 p-4 bg-white rounded-lg shadow-md hover:shadow-lg transition-shadow"> <!-- Large payload repeated for every list item --> </div>
With 100 items in a list, this verbose markup significantly impacts performance.
The Solution
Blade Tailwind Extract extracts your Tailwind classes into a CSS file using @apply, replacing them with short class names:
Development Mode (Injected):
<div class="__card-wrapper__ flex flex-col gap-2 p-4 bg-white rounded-lg shadow-md __"> <!-- Easy to edit inline --> </div>
Production Mode (Extracted):
<div class="TW-a40f-card-wrapper"> <!-- Minimal wire payload --> </div>
Generated CSS:
.TW-a40f-card-wrapper { @apply flex flex-col gap-2 p-4 bg-white rounded-lg shadow-md; }
The a40f hash is derived from the file path, ensuring no conflicts between files.
Features
- ✅ Reduces Livewire wire payload - Short class names instead of verbose Tailwind strings
- ✅ Automated class identification - Wrap command finds and marks repeated class lists
- ✅ Intelligent deduplication - Identical class lists share the same wrapper name
- ✅ Comprehensive pattern support - Works with
class="",wire:class="",:class(static & ternary),x-bind:class,@class([])(simple & conditional) - ✅ Bidirectional - Extract for production, restore for development
- ✅ Safe - Reserved classes like
group,group/, andpeerare automatically skipped with informative warnings - ✅ Collision-free - File-based hashing prevents class name conflicts
- ✅ Pattern matching - Process specific files or entire directories
- ✅ Configurable - Customize prefix, hash length, output path, and more
Installation
Install via Composer as a dev dependency (this is a development tool, not needed in production):
composer require dxgx/blade-tailwind-extract --dev
Publish the configuration file (optional):
php artisan vendor:publish --tag=blade-tailwind-extract-config
Usage
Quick Start: 3-Step Workflow
- Wrap - Automatically identify and mark class lists with semantic names
- Extract - Convert marked classes into compact CSS
- Restore - Bring back inline classes for editing
Wrap Classes (Preparation Step)
Before extraction, use the wrap command to automatically identify and mark repeated Tailwind class lists with semantic wrapper names. This helps you see which classes will be extracted and ensures consistent naming for identical class lists.
# Wrap classes in a specific file php artisan dg:blade-tailwind:wrap resources/views/components/card.blade.php # Wrap classes in an entire directory (recursive) php artisan dg:blade-tailwind:wrap ./resources/views/livewire # Wrap classes using a pattern php artisan dg:blade-tailwind:wrap "*preview*" php artisan dg:blade-tailwind:wrap "*card*.blade.php" # Wrap multiple files or patterns (comma-separated) php artisan dg:blade-tailwind:wrap "card.blade.php,list.blade.php" php artisan dg:blade-tailwind:wrap "*preview*,*card*" # Wrap all files in search_path (with confirmation prompts) php artisan dg:blade-tailwind:wrap # Skip confirmations for automated workflows php artisan dg:blade-tailwind:wrap --yy # Preview changes without modifying files php artisan dg:blade-tailwind:wrap "*preview*" --dry-run # Customize minimum class count (default: 3) php artisan dg:blade-tailwind:wrap components/card.blade.php --min=4 # Skip class lists containing specific prefix (default: TW) php artisan dg:blade-tailwind:wrap components/card.blade.php --skip-prefix=CUSTOM
What it does:
- Scans for class lists with 3+ classes (configurable via
--min) - Automatically deduplicates: identical class lists get the same wrapper name
- Generates semantic names:
__adjective-noun-number__(e.g.,__happy-cat-1__) - Skips patterns that should never be extracted:
__,material-symbols-outlined,TW- - Shows summary of changes with occurrence counts
Before wrapping:
<div class="flex items-center justify-between gap-4 p-4">Item 1</div> <div class="flex items-center justify-between gap-4 p-4">Item 2</div> <div class="bg-blue-500 text-white rounded p-2">Button</div>
After wrapping:
<div class="__happy-cat-1__ flex items-center justify-between gap-4 p-4 __">Item 1</div> <div class="__happy-cat-1__ flex items-center justify-between gap-4 p-4 __">Item 2</div> <div class="__quick-fox-2__ bg-blue-500 text-white rounded p-2 __">Button</div>
Notice how identical class lists share the same wrapper name (happy-cat-1), while different lists get unique names.
Supported Class Attribute Patterns
The wrap command supports all major Blade/Livewire/Alpine.js class attribute patterns:
1. Static class Attribute
Standard HTML class attribute:
<!-- Before --> <div class="flex items-center justify-between gap-4 p-4">Content</div> <!-- After wrapping --> <div class="__happy-cat-1__ flex items-center justify-between gap-4 p-4 __">Content</div>
2. Livewire wire:class Attribute
Livewire's reactive class binding:
<!-- Before --> <div wire:class="flex items-center space-x-2 p-4">Content</div> <!-- After wrapping --> <div wire:class="__sunny-owl-2__ flex items-center space-x-2 p-4 __">Content</div>
3. Alpine.js :class with Static Classes
Dynamic class binding with static string:
<!-- Before --> <button :class="flex items-center justify-center rounded-md p-2">Click</button> <!-- After wrapping --> <button :class="__quick-fox-3__ flex items-center justify-center rounded-md p-2 __">Click</button>
4. Alpine.js :class with Ternary Expression
Conditional class switching with ternary operator:
<!-- Before --> <button :class="isActive ? 'bg-blue-500 text-white border-blue-500 hover:bg-blue-700' : 'bg-white text-gray-700 border-gray-300 hover:bg-gray-100'"> Toggle </button> <!-- After wrapping (both branches wrapped independently) --> <button :class="isActive ? '__brave-ant-4__ bg-blue-500 text-white border-blue-500 hover:bg-blue-700 __' : '__calm-bee-5__ bg-white text-gray-700 border-gray-300 hover:bg-gray-100 __'"> Toggle </button>
5. Alpine.js x-bind:class Attribute
Explicit Alpine binding syntax:
<!-- Before --> <div x-bind:class="flex flex-col gap-4 p-6">Content</div> <!-- After wrapping --> <div x-bind:class="__lazy-ram-6__ flex flex-col gap-4 p-6 __">Content</div>
6. Blade @class Directive (Simple Array)
Blade's class directive with simple string:
<!-- Before --> <div @class(['flex items-center justify-between p-4 border rounded'])>Item</div> <!-- After wrapping --> <div @class(['__eager-jay-7__ flex items-center justify-between p-4 border rounded __'])>Item</div>
7. Blade @class Directive (Conditional Array)
Blade's class directive with conditional classes:
<!-- Before --> <button @class([ 'flex items-center justify-center size-9', 'text-gray-200 cursor-not-allowed opacity-50' => !$isEnabled, 'text-blue-500 hover:text-blue-600 hover:bg-blue-50' => $isEnabled ])>Action</button> <!-- After wrapping (all qualifying strings wrapped) --> <button @class([ '__gentle-fox-8__ flex items-center justify-center size-9 __', '__jolly-cat-9__ text-gray-200 cursor-not-allowed opacity-50 __' => !$isEnabled, '__kind-dog-10__ text-blue-500 hover:text-blue-600 hover:bg-blue-50 __' => $isEnabled ])>Action</button>
8. Combined Static and Dynamic Classes
Elements with both static class and dynamic :class attributes (wrapped independently):
<!-- Before --> <button :class="active ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-700'" class="flex items-center justify-center rounded-md"> Toggle </button> <!-- After wrapping --> <button :class="active ? 'bg-blue-500 text-white' : 'bg-gray-200 text-gray-700'" class="__lively-hen-11__ flex items-center justify-center rounded-md __"> Toggle </button>
Note: In this example, the ternary branches have only 2 classes each (below the default minimum of 3), so they aren't wrapped. The static class attribute with 4 classes is wrapped.
Pattern Behavior Summary
- All patterns respect the minimum class count (default: 3, configurable via
--min) - Identical class lists across any pattern type share the same wrapper name
- Already wrapped class lists are skipped (won't double-wrap)
- Protected patterns (containing
__,material-symbols-outlined, or starting withTW-) are never wrapped - Ternary expressions have both branches examined and wrapped independently if they meet the threshold
- Conditional arrays in
@classhave all string values examined and wrapped independently - Static and dynamic attributes on the same element are processed independently
Special Cases Handled
The wrap command correctly handles complex Tailwind patterns:
Arbitrary values with quotes:
<!-- Classes with arbitrary values containing quotes are fully supported --> <div class="after:content-[''] before:content-['>'] w-full">Content</div> <!-- After wrapping (closing __ correctly placed at end) --> <div class="__happy-cat-1__ after:content-[''] before:content-['>'] w-full __">Content</div>
Reserved classes (automatically skipped during extraction):
- Classes containing
group,group/, orpeercan be wrapped but won't be extracted - The extract command will skip these patterns with informative warnings
- This prevents breaking Tailwind's parent-child and sibling selectors
Extract Classes (Development → Production)
The target parameter accepts multiple formats:
0. No target (processes all files in search_path with confirmation):
# Prompts for confirmation before processing all .blade.php files php artisan dg:blade-tailwind:extract # Shows: # 1. First confirmation: "Are you sure you want to process X file(s)?" # 2. List of files to be processed (max 50 shown) # 3. Second confirmation: "Proceed with extract operation?" # Cancel at any prompt to abort
1. Directory (processes all .blade.php files recursively):
php artisan dg:blade-tailwind:extract ./resources/views php artisan dg:blade-tailwind:extract ./resources/views/components
2. Single file path:
php artisan dg:blade-tailwind:extract resources/views/livewire/garage/list/item.blade.php php artisan dg:blade-tailwind:extract components/card.blade.php
3. Pattern matching (searches in configured search_path):
php artisan dg:blade-tailwind:extract *preview* php artisan dg:blade-tailwind:extract *card*.blade.php php artisan dg:blade-tailwind:extract list-item
4. Multiple targets (comma-separated, can mix patterns and files):
php artisan dg:blade-tailwind:extract image-preview.blade.php,card-list.blade.php php artisan dg:blade-tailwind:extract *preview*,*card*,header.blade.php
Restore Classes (Production → Development)
Restore Tailwind classes for editing (accepts same target formats as extract):
# No target (with confirmation prompts) php artisan dg:blade-tailwind:restore # Directory php artisan dg:blade-tailwind:restore ./resources/views # Pattern php artisan dg:blade-tailwind:restore *preview* # Specific file php artisan dg:blade-tailwind:restore components/card.blade.php # Multiple php artisan dg:blade-tailwind:restore *card*,*list*
Automatic CSS Cleanup: When you restore classes, the command automatically removes the restored CSS rules from the output file, keeping your CSS clean and preventing stale rules from accumulating.
Workflow
Option A: Automated (New Files)
- Wrap your Blade file to identify and mark repeated class lists
- Extract marked classes to generate compact CSS
- Commit both Blade files and CSS file
Option B: Manual (Existing Workflow)
- Manually add
__name__ classes __markers around classes you want to extract - Extract to generate compact CSS
- Commit both Blade files and CSS file
Option C: Editing Existing Extracted Files
- Restore before editing (restore Tailwind classes to inline format)
- Edit your Blade files normally
- Extract after editing (compact back to short class names)
- Commit both Blade files and CSS file
You can add markers manually or use the wrap command to add them automatically.
Manual approach: Wrap classes you want to extract with double underscores:
<div class="__card-wrapper__ flex flex-col gap-2 p-4 __"> <!-- Content --> </div>
Automated approach: Use the wrap command to automatically identify and mark class lists:
php artisan dg:blade-tailwind:wrap your.view.namelass="__card-wrapper__ flex flex-col gap-2 p-4 __"> <!-- Content --> </div>
After extraction:
<div class="TW-a40f-card-wrapper"> <!-- Content --> </div>
Important: The group and peer Tailwind classes cannot be extracted (they break parent-child selectors). The tool will warn and skip them automatically.
Configuration
Configuration file: config/dg-blade-tailwind-extract.php
return [ // CSS output file path 'css_output_path' => resource_path('css/tw-extracted.css'), // Class prefix (default: TW) 'class_prefix' => 'TW', // Hash length for file-based collision avoidance (default: 4) 'hash_length' => 4, // Default search path when using pattern matching 'search_path' => resource_path('views'), // Directories to ignore during file scanning 'ignored_directories' => [ './vendor/', 'vendor/', './node_modules/', 'node_modules/', ], // Reserved Tailwind classes that cannot be extracted // These use parent-child selectors that break when extracted to @apply // Also checks for variants like 'group/' and 'peer/' 'reserved_classes' => [ 'group', 'peer', ], // Safety limit for extraction loops 'max_iterations' => 10, ];
Note: When the extract command encounters patterns containing reserved classes (like group, group/item, or peer), it will:
- Skip extraction for that pattern
- Display an informative warning showing which patterns were skipped and why
- Continue processing other patterns in the same file
Advanced Usage
Custom CSS Output Path
php artisan dg:blade-tailwind:extract ./resources/views --css-file=resources/css/my-custom.css
Working with Livewire Components
The tool works seamlessly with Livewire's ->class([...]) method and Blade's @class([...]) directive:
Before Extraction:
<div @class([ '__item-wrapper__ flex gap-2 p-4 __', 'bg-green-50' => $selected, ])>
After Extraction:
<div @class([ 'TW-a40f-item-wrapper', 'bg-green-50' => $selected, ])>
Wrap Mode (Optional Preparation):**
- Scans for class lists with minimum number of classes (default: 3)
- Automatically deduplicates identical class lists
- Generates semantic wrapper names:
__adjective-noun-number__ - Marks class lists:
__name__ classes __ - Skips protected patterns (material-symbols-outlined, TW-, etc.)
- Shows summary with occurrence counts
-
Extract Mode:
- Scans for
__name__ tailwind classes __patterns - Generates short class names:
{PREFIX}-{HASH}-{NAME} - Writes
@applyrules to the CSS file - Replaces inline classes with short names
- Scans for
-
Restore Mode:
- Reads existing
@applyrules from CSS file - Finds short class names in Blade files
- Restores original Tailwind class strings
- Reads existing
-
Extract Mode:
- Scans for
__name__ tailwind classes __patterns - Generates short class names:
{PREFIX}-{HASH}-{NAME} - Writes
@applyrules to the CSS file - Replaces inline classes with short names
- Scans for
-
Restore Mode:
- Reads existing
@applyrules from CSS file - Finds short class names in Blade files
- Restores original Tailwind class strings
- Reads existing
-
File Hash:
- Each file gets a unique 4-character hash (configurable)
- Prevents class name collisions between files
- Allows same semantic name (
card-wrapper) across different files
Gotchas & Best Practices
- ⚠️ Never manually edit
TW-*class names in extracted files - ⚠️ Always restore → edit → extract workflow
- ⚠️ Don't extract
group,peer, or other parent-modifier classes - ✅ Commit both Blade and CSS files together
- ✅ Run extract before deploying to production
- ✅ Run restore before starting development
Testing
composer test
Security
If you discover any security-related issues, please email packages@dxgx.dev instead of using the issue tracker.
Credits
License
The MIT License (MIT). Please see License File for more information.
Changelog
Please see CHANGELOG.md for recent changes.
Contributing
Please see CONTRIBUTING.md for details.
dxgx/blade-tailwind-extract 适用场景与选型建议
dxgx/blade-tailwind-extract 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 65 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 05 月 18 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「performance」 「laravel」 「blade」 「tailwindcss」 「livewire」 「dxgx」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 dxgx/blade-tailwind-extract 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 dxgx/blade-tailwind-extract 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 dxgx/blade-tailwind-extract 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
g4 application profiler package
CSS/Javascript Minificator, Compressor and Concatenator for TYPO3 - highly configurable frontend asset optimization for CSS/JS merging, minification and compression with optional body parsing, async/defer loading, inline output, data-ignore exclusions, SRI integrity validation/calculation, external
WordPress mu-plugin to remove jQuery Migrate from the list of jQuery dependencies and to allow jQuery to enqueue before </body> instead of in the <head>.
Create link to static resources with cache-breaking segment based on md5 of the file
Production-ready Laravel Prometheus metrics package with built-in collectors for HTTP, database, cache, queue, events, errors, filesystem, and mail monitoring
Alfabank REST API integration
统计信息
- 总下载量: 65
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 12
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-05-18