
# 변형

`variants()`는 PHP [CVA(Class Variance Authority)](https://github.com/joe-bell/cva) 포트입니다. 기본 클래스, 이름이 지정된 변형 축, 기본값으로 구성된 선언형 설정을 호출 가능한 함수로 변환하며, 이 함수는 props를 클래스 문자열로 매핑합니다. 크기나 의도처럼 여러 스타일 속성을 조합하는 컴포넌트에 사용하세요.

```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()`는 `callable`를 반환하며, props 배열(또는 인수 없음)을 전달하면 해석된 문자열을 얻습니다.

## 구성

| 키 | 유형 | 용도 |
|-----|------|---------|
| `base` | `string` | 모든 출력에 적용되는 클래스. |
| `variants` | `array` | 이름이 지정된 축으로, 각 축은 값 이름을 클래스 문자열에 매핑합니다. |
| `compoundVariants` | `array` | 여러 변형 값이 조합될 때만 적용되는 클래스 문자열. |
| `defaultVariants` | `array` | prop을 생략했을 때 사용하는 축별 값. |

모든 키는 선택 사항입니다. `variants`가 없으면 결과에 `base`와 사용자 지정 클래스가 포함됩니다:

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

변형 값에 `null`를 지정하면 아무것도 추가하지 않습니다. 축에서 명시적으로 "설정 해제" 옵션을 만들 때 유용합니다.

## 결과 호출

CVA 방식으로 props 배열 하나를 전달하세요. 키는 변형 축 이름과 일치하며, 생략한 축에는 `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'
```

`class` 키(별칭 `className`)는 출력의 끝에 추가됩니다. 추가만으로는 충돌이 해결되지 않습니다. 아래의 컴포넌트 패턴을 참고하세요.

## compoundVariants

`compoundVariants`의 각 항목은 변형 조건 배열과 추가 클래스가 담긴 `class`(또는 `className`)로 구성됩니다. 모든 조건이 해석된 props와 일치할 때만 클래스가 적용되며, 기본값도 포함해 확인합니다. 조건 값은 단일 문자열이거나 여러 값 중 하나와 일치하도록 하는 문자열 배열일 수 있습니다.

```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'
```

## 컴포넌트 패턴

`variants()`는 `class`만 추가합니다. 호출자가 변형 기본값을 실제로 재정의할 수 있게 하려면 출력을 [cn()](https://tailwindphp.inline0.dev/ko/docs/class-utilities)로 감싸 충돌 시 호출자의 값이 우선 적용되게 하세요.

```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']);
```

`static $styles` 캐시는 변형 구성을 한 번 생성한 뒤 여러 호출에서 재사용합니다.

## compose()

`compose()`는 여러 변형 컴포넌트를 하나로 병합합니다. 조합된 호출 가능 함수는 각 prop을 모든 컴포넌트에 전달하고, 각 출력을 모은 다음 `class`/`className`를 추가합니다.

```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'
```

이렇게 하면 재사용 가능한 축(간격, 입체감, 레이아웃)을 별도의 정의로 유지하면서 컴포넌트별로 조합할 수 있습니다.

## 가져오기

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

둘 다 기본 `TailwindPHP` 네임스페이스에 있으며 패키지에 포함됩니다. Joe Bell이 만든 CVA의 PHP 포트입니다.
