
# Varian

`variants()` adalah port PHP dari [CVA (Class Variance Authority)](https://github.com/joe-bell/cva). Fitur ini mengubah konfigurasi deklaratif — base, sumbu varian bernama, dan nilai default — menjadi fungsi yang memetakan props ke string kelas. Gunakan saat komponen memiliki dimensi gaya seperti ukuran dan tujuan yang perlu digabungkan.

```php
use function TailwindPHP\variants;

$button = variants([
    'base' => 'btn font-semibold border rounded',
    'variants' => [
        'intent' => [
            'primary'   => 'bg-blue-500 text-white',
            'secondary' => 'bg-gray-200 text-gray-800',
        ],
        'size' => [
            'sm' => 'text-sm px-2 py-1',
            'md' => 'text-base px-4 py-2',
        ],
    ],
    'defaultVariants' => [
        'intent' => 'primary',
        'size'   => 'md',
    ],
]);

$button();
// => 'btn font-semibold border rounded bg-blue-500 text-white text-base px-4 py-2'
```

`variants()` mengembalikan `callable`; panggil dengan array props (atau tanpa argumen) untuk mendapatkan string yang telah diselesaikan.

## Konfigurasi

| Kunci | Tipe | Tujuan |
|-----|------|---------|
| `base` | `string` | Kelas yang diterapkan ke setiap keluaran. |
| `variants` | `array` | Sumbu bernama, masing-masing memetakan nama nilai ke string kelas. |
| `compoundVariants` | `array` | String kelas yang diterapkan hanya saat beberapa nilai varian digabungkan. |
| `defaultVariants` | `array` | Nilai untuk tiap sumbu yang digunakan jika prop tidak diberikan. |

Semua kunci bersifat opsional. Tanpa `variants`, hasilnya mengembalikan `base` beserta kelas kustom apa pun:

```php
$x = variants(['base' => 'box']);
$x();                          // => 'box'
$x(['class' => 'p-4']);        // => 'box p-4'
```

Nilai varian dapat dipetakan ke `null` agar tidak menyumbangkan apa pun — berguna sebagai opsi eksplisit "unset" pada suatu sumbu.

## Memanggil hasilnya

Berikan satu array props, seperti pada CVA. Kunci harus cocok dengan nama sumbu varian; sumbu yang tidak diberikan menggunakan `defaultVariants`.

```php
$button(['intent' => 'secondary']);
// => 'btn font-semibold border rounded bg-gray-200 text-gray-800 text-base px-4 py-2'

$button(['size' => 'sm', 'class' => 'mt-4']);
// => 'btn font-semibold border rounded bg-blue-500 text-white text-sm px-2 py-1 mt-4'
```

Kunci `class` (dan aliasnya `className`) ditambahkan di akhir keluaran. Penambahan saja tidak menyelesaikan konflik — lihat pola komponen di bawah.

## compoundVariants

Setiap entri dalam `compoundVariants` berupa array kondisi varian beserta `class` (atau `className`) kelas tambahan. Kelas tersebut diterapkan hanya jika setiap kondisi cocok dengan props yang telah diselesaikan, termasuk nilai default. Nilai kondisi dapat berupa satu string atau array string untuk mencocokkan beberapa nilai.

```php
$button = variants([
    'base' => 'btn',
    'variants' => [
        'intent' => [
            'primary' => 'bg-blue-500',
            'warning' => 'bg-yellow-500',
            'danger'  => 'bg-red-500',
        ],
        'size' => ['sm' => 'text-sm', 'md' => 'text-base'],
    ],
    'compoundVariants' => [
        // Applies only when intent=primary AND size=md
        ['intent' => 'primary', 'size' => 'md', 'class' => 'uppercase'],
        // Applies when intent is warning OR danger
        ['intent' => ['warning', 'danger'], 'class' => '!border-red-500'],
    ],
    'defaultVariants' => ['intent' => 'primary', 'size' => 'md'],
]);

$button();
// => 'btn bg-blue-500 text-base uppercase'

$button(['intent' => 'danger']);
// => 'btn bg-red-500 text-base !border-red-500'
```

## Pola komponen

`variants()` hanya menambahkan `class`; agar pemanggil benar-benar dapat meng-override nilai default varian, bungkus keluarannya dengan [cn()](https://tailwindphp.inline0.dev/id/docs/class-utilities) agar konflik diselesaikan dengan mengutamakan pemanggil.

```php
use function TailwindPHP\variants;
use function TailwindPHP\cn;

function Button(array $props = []): string {
    static $styles = null;
    $styles ??= variants([
        'base' => 'inline-flex items-center justify-center rounded-md font-medium',
        'variants' => [
            'variant' => [
                'default' => 'bg-primary text-primary-foreground hover:bg-primary/90',
                'outline' => 'border border-input bg-background hover:bg-accent',
                'ghost'   => 'hover:bg-accent hover:text-accent-foreground',
            ],
            'size' => [
                'default' => 'h-10 px-4 py-2',
                'sm'      => 'h-9 px-3',
                'lg'      => 'h-11 px-8',
            ],
        ],
        'defaultVariants' => ['variant' => 'default', 'size' => 'default'],
    ]);

    // cn() merges variant output with custom classes, resolving conflicts
    $class = cn($styles($props), $props['class'] ?? null);
    $text  = $props['children'] ?? 'Button';
    return "<button class=\"{$class}\">{$text}</button>";
}

// The custom px-8 wins over the variant's px-3 via cn()
Button(['variant' => 'outline', 'size' => 'sm', 'class' => 'mt-4 px-8']);
```

Cache `static $styles` menyusun konfigurasi varian sekali dan menggunakannya kembali pada pemanggilan berikutnya.

## compose()

`compose()` menggabungkan beberapa komponen varian menjadi satu. Fungsi gabungan meneruskan setiap prop ke semua komponen, mengumpulkan keluarannya, lalu menambahkan `class`/`className` apa pun.

```php
use function TailwindPHP\variants;
use function TailwindPHP\compose;

$box   = variants(['variants' => ['shadow' => ['sm' => 'shadow-sm', 'md' => 'shadow-md']]]);
$stack = variants(['variants' => ['gap' => ['1' => 'gap-1', '2' => 'gap-2']]]);
$card  = compose($box, $stack);

$card(['shadow' => 'md', 'gap' => '2']);
// => 'shadow-md gap-2'
```

Cara ini menjaga sumbu yang dapat digunakan kembali (jarak, elevasi, tata letak) dalam definisi terpisah, lalu menggabungkannya untuk tiap komponen.

## Impor

```php
use function TailwindPHP\variants;
use function TailwindPHP\compose;
```

Keduanya tersedia di namespace `TailwindPHP` utama dan disertakan dalam paket — port PHP CVA oleh Joe Bell.
