diff --git a/README.md b/README.md index f7b5697..8acceeb 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,30 @@ button({ w: 350 }); button({ w: "w-full" }); ``` +## Merging External `className` Safely (wcn) + +WindCtrl resolves Tailwind class conflicts **inside** `windctrl()` using `tailwind-merge`. +However, in real applications you often need to merge **additional `className` values** at the component boundary. + +A simple string concat can reintroduce conflicts: + +```tsx +// ⚠️ Can cause subtle Tailwind conflicts (e.g. p-2 vs p-4) +className={`${result.className} ${className}`} +``` + +WindCtrl exports a small helper for this use case: + +```tsx +import { wcn } from "windctrl"; + +// ✅ Conflict-safe merge +className={wcn(result.className, className)} +``` + +`wcn()` is equivalent to `twMerge(clsx(...))` and matches WindCtrl’s internal conflict resolution behavior. +This keeps the “last one wins” behavior consistent across both generated and user-supplied classes. + ## Core Concepts ### Variants diff --git a/examples/Button.tsx b/examples/Button.tsx index ae6d802..363e6ea 100644 --- a/examples/Button.tsx +++ b/examples/Button.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { windctrl, dynamic as d } from "../src/index"; +import { windctrl, dynamic as d, wcn } from "../src/index"; import type { ComponentPropsWithoutRef, ElementType } from "react"; const button = windctrl({ @@ -71,7 +71,7 @@ export function Button({ return ( diff --git a/src/index.test.ts b/src/index.test.ts index 7e7a825..ff0dfc9 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "vitest"; -import { windctrl, wc, dynamic as d } from "./"; +import { windctrl, wc, dynamic as d, wcn } from "./"; describe("wc", () => { it("should be the same as windctrl", () => { @@ -7,6 +7,20 @@ describe("wc", () => { }); }); +describe("wcn", () => { + it("should merge class names with clsx behavior", () => { + expect(wcn("a", false && "b", null as any, undefined, "c")).toBe("a c"); + }); + + it("should resolve Tailwind conflicts using tailwind-merge (last one wins)", () => { + expect(wcn("p-2", "p-4")).toBe("p-4"); + }); + + it("should handle arrays and objects like clsx", () => { + expect(wcn(["a", ["b"]], { c: true, d: false })).toBe("a b c"); + }); +}); + describe("windctrl", () => { describe("Base classes", () => { it("should apply base classes when provided", () => { diff --git a/src/index.ts b/src/index.ts index 8ac8ed9..b1caa2f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -385,3 +385,7 @@ export function windctrl< } export const wc = windctrl; + +export function wcn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +}