PHPackages                             eurym3d0n/tailwind-engine - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. eurym3d0n/tailwind-engine

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

eurym3d0n/tailwind-engine
=========================

Enterprise-grade PHP 8.3 package for Tailwind CSS 4 class merging, conflict resolution, and sorting.

v1.1.0(2mo ago)14MITPHPPHP &gt;=8.5CI passing

Since May 2Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/Eurym3d0n/TailwindEngine)[ Packagist](https://packagist.org/packages/eurym3d0n/tailwind-engine)[ Docs](https://fox-trotte.fr)[ Fund](https://www.buymeacoffee.com/xen0risdev)[ GitHub Sponsors](https://github.com/Eurym3d0n)[ RSS](/packages/eurym3d0n-tailwind-engine/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (1)Versions (2)Used By (0)

TailwindEngine
==============

[](#tailwindengine)

Enterprise-grade PHP 8.5 package for merging, conflict-resolving, and sorting Tailwind CSS 4 class strings.

Replaces ad-hoc string concatenation and `implode()` calls with a typed, tested, and extensible pipeline that mirrors the behaviour of the official Tailwind CSS Prettier plugin.

---

Requirements
------------

[](#requirements)

- PHP 8.5 or higher
- No runtime dependencies

---

Installation
------------

[](#installation)

```
composer require eurym3d0n/tailwind-engine:dev-main
```

---

Quick start
-----------

[](#quick-start)

```
use TailwindEngine\DSL\Tailwind;

$classes = Tailwind::new()
    ->add('flex items-center gap-4')
    ->add('mt-2', 'mt-8')          // conflict: mt-8 wins
    ->add('text-lg', 'text-white') // no conflict: different families
    ->build();

echo $classes;
// flex items-center gap-4 text-lg text-white mt-8
```

---

Core concepts
-------------

[](#core-concepts)

### Families

[](#families)

Every Tailwind utility belongs to a **family** — the named group that defines which utilities conflict with each other. Two utilities in the same family with the same variant prefix conflict: the last declaration wins, mirroring CSS cascade behaviour.

```
// mt-4 and mt-8 are in the same family ('mt-') with no variant prefix.
// mt-8 overwrites mt-4.
Tailwind::new()->add('mt-4', 'mt-8')->build(); // "mt-8"

// mt-4 and mb-4 are in different families ('mt-' vs 'mb-').
// Both survive.
Tailwind::new()->add('mt-4', 'mb-4')->build(); // "mt-4 mb-4"

// text-lg (family: text-size) and text-red-500 (family: text-color)
// are in different families. Both survive.
Tailwind::new()->add('text-lg', 'text-red-500')->build(); // "text-lg text-red-500"
```

### Variants

[](#variants)

Variant prefixes (`hover:`, `dark:`, `lg:`, etc.) are part of the conflict key. A variant-prefixed token never conflicts with its base counterpart.

```
// 'mt-4' and 'hover:mt-8' do not conflict.
Tailwind::new()->add('mt-4', 'hover:mt-8')->build(); // "mt-4 hover:mt-8"

// 'hover:mt-4' and 'hover:mt-8' do conflict (same family, same variant).
Tailwind::new()->add('hover:mt-4', 'hover:mt-8')->build(); // "hover:mt-8"
```

### Sort order

[](#sort-order)

The compiled string follows the official Tailwind CSS Prettier plugin ordering: layout and structure first, effects and interactivity last. Base utilities always precede their variant-prefixed counterparts.

```
Tailwind::new()
    ->add('hover:mt-4', 'bg-blue-600', 'flex', 'mt-2')
    ->build();
// "flex mt-2 bg-blue-600 hover:mt-4"
```

---

API reference
-------------

[](#api-reference)

### `Tailwind` — fluent builder

[](#tailwind--fluent-builder)

The primary public-facing API. Each method returns a new immutable instance, making partial configurations safely reusable.

```
use TailwindEngine\DSL\Tailwind;

// Static constructor (zero-configuration, no DI container required)
$builder = Tailwind::new();

// Append one or more class strings (multi-token strings are accepted)
$builder = $builder->add('flex', 'items-center gap-4');

// Conditionally append classes
$builder = $builder->addWhen($isActive, 'ring-2 ring-blue-500');
$builder = $builder->addUnless($isDisabled, 'cursor-pointer');

// Compile and return a TailwindString (implements Stringable)
$result = $builder->build();

echo $result;           // use in string context directly
(string) $result;       // explicit cast
$result->toString();    // explicit method
```

#### Immutability in practice

[](#immutability-in-practice)

Because every `add*` method returns a new instance, a base configuration can be derived into multiple variants without mutation:

```
$base    = Tailwind::new()->add('inline-flex items-center gap-2 rounded-lg px-4 py-2 font-semibold transition');
$primary = $base->add('bg-blue-600 text-white hover:bg-blue-700');
$danger  = $base->add('bg-red-600 text-white hover:bg-red-700');

// $base is unaffected.
echo $primary->build(); // "inline-flex items-center ... bg-blue-600 text-white hover:bg-blue-700"
echo $danger->build();  // "inline-flex items-center ... bg-red-600 text-white hover:bg-red-700"
```

### `TailwindEngine` — direct engine usage

[](#tailwindengine--direct-engine-usage)

For applications that manage their own class list lifecycle (e.g., building `TailwindClassList` incrementally from multiple sources):

```
use TailwindEngine\Core\TailwindEngineFactory;
use TailwindEngine\Support\ValueObject\TailwindClassList;

$engine = TailwindEngineFactory::create();

$classes = new TailwindClassList(['flex items-center', 'bg-blue-600', 'mt-4 mt-8']);
$result  = $engine->compile($classes);

echo $result; // "flex items-center bg-blue-600 mt-8"
```

---

Extending the registry
----------------------

[](#extending-the-registry)

The family registry is split into domain-specific traits. To add custom utility families, subclass `FamilyRegistry` and override the two methods:

```
use TailwindEngine\Core\FamilyRegistry;

class MyRegistry extends FamilyRegistry
{
    public function getFamilies(): array
    {
        return array_merge(parent::getFamilies(), [
            'my-custom-' => null,
            'my-exact'   => ['my-exact-sm', 'my-exact-lg'],
        ]);
    }

    public function getSortOrder(): array
    {
        // Insert the custom families at the desired sort position.
        $order = parent::getSortOrder();
        $insertAfter = array_search('opacity-', $order, true);

        array_splice($order, $insertAfter + 1, 0, ['my-custom-', 'my-exact']);

        return $order;
    }
}
```

Then wire the custom registry into the engine:

```
use TailwindEngine\Core\TailwindEngineFactory;
use TailwindEngine\DSL\Tailwind;

$engine  = TailwindEngineFactory::create(new MyRegistry());
$builder = new Tailwind($engine);

echo $builder->add('my-custom-sm', 'my-custom-lg')->build();
// "my-custom-lg" (conflict resolved, last wins)
```

---

Architecture
------------

[](#architecture)

```
src/
  Contract/
    ClassFlattenerInterface.php
    ClassSorterInterface.php
    ConflictResolverInterface.php
    FamilyResolverInterface.php
  Core/
    Registry/
      AccessibilityFamilies.php
      LayoutFamilies.php
      FlexboxFamilies.php
      GridFamilies.php
      AlignmentFamilies.php
      SizingFamilies.php
      SpacingFamilies.php
      TypographyFamilies.php
      BackgroundFamilies.php
      BorderFamilies.php
      EffectFamilies.php
      FilterFamilies.php
      TransformFamilies.php
      TransitionFamilies.php
      InteractivityFamilies.php
      SvgFamilies.php
      ColumnFamilies.php
    FamilyRegistry.php
    FamilyResolver.php
    ClassFlattener.php
    ConflictResolver.php
    ClassSorter.php
    TailwindEngine.php
    TailwindEngineFactory.php
  DSL/
    Tailwind.php
  Support/
    ValueObject/
      TailwindClassList.php
      TailwindString.php

```

### Pipeline

[](#pipeline)

```
Tailwind::add()
    └── TailwindClassList (immutable accumulator)
            └── TailwindEngine::compile()
                    ├── ClassFlattener   — splits strings into individual tokens
                    ├── ConflictResolver — last-wins deduplication by family + variant key
                    └── ClassSorter      — reorders by SORT_ORDER from FamilyRegistry
                            └── TailwindString (Stringable result)

```

### Design decisions

[](#design-decisions)

**Two sources of truth in `FamilyRegistry`.** `getFamilies()` and `getSortOrder()` are intentionally separate. The merge order in `getFamilies()`governs prefix-matching accuracy (more specific prefixes must precede broader ones within each domain trait). `getSortOrder()` governs the compiled output order independently, so a family can be declared in one domain but sorted into another without any coupling between the two concerns.

**Domain traits, not a monolithic list.** Each `*Families` trait owns one domain. This makes the registry navigable, independently testable, and trivially extensible without touching a 300-line constant.

**Immutable value objects throughout.** `TailwindClassList`, `TailwindString`, and the `Tailwind` builder are all immutable. Base configurations can be derived into variants safely, and compiled results can be stored without defensive copying.

**Contracts for every pipeline stage.** `ClassFlattenerInterface`, `ConflictResolverInterface`, `ClassSorterInterface`, and `FamilyResolverInterface` allow any stage to be replaced or decorated (e.g., wrapping `ConflictResolver` with a caching decorator) without touching `TailwindEngine`.

---

Running the tests
-----------------

[](#running-the-tests)

```
composer install
./vendor/bin/phpunit
```

The test suite is structured in three layers:

- `tests/Core/` — unit tests for each pipeline stage in isolation.
- `tests/DSL/` — unit tests for the fluent builder API.
- `tests/Integration/` — end-to-end tests asserting on the final compiled string.

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance83

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity51

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

84d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/68976124?v=4)[Jessy LELIEVRE](/maintainers/Eurym3d0n)[@Eurym3d0n](https://github.com/Eurym3d0n)

---

Top Contributors

[![Eurym3d0n](https://avatars.githubusercontent.com/u/68976124?v=4)](https://github.com/Eurym3d0n "Eurym3d0n (6 commits)")

---

Tags

phpcsstailwindclass-merger

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/eurym3d0n-tailwind-engine/health.svg)

```
[![Health](https://phpackages.com/badges/eurym3d0n-tailwind-engine/health.svg)](https://phpackages.com/packages/eurym3d0n-tailwind-engine)
```

###  Alternatives

[awssat/tailwindo

Convert Bootstrap CSS to Tailwind CSS

1.1k20.4k1](/packages/awssat-tailwindo)[yieldstudio/tailwind-merge-php

Merge Tailwind CSS classes without style conflicts

4975.4k1](/packages/yieldstudio-tailwind-merge-php)[tailwindphp/tailwindphp

A full port of TailwindCSS 4.x to PHP

193.0k4](/packages/tailwindphp-tailwindphp)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
