Skip to content

Commit 806458e

Browse files
committed
tweak README
1 parent 8747894 commit 806458e

1 file changed

Lines changed: 28 additions & 9 deletions

File tree

README.md

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ It evolves the concept of Variant APIs (like [cva](https://cva.style/)) by intro
1111
- 🎨 **Unified API** - Seamlessly blends static Tailwind classes and dynamic inline styles into one cohesive interface.
1212
- 🧩 **Trait System** - Solves combinatorial explosion by treating states as stackable, non-exclusive layers.
1313
- 🎯 **Scoped Styling** - Context-aware styling using data attributes - no React Context required (RSC friendly).
14-
-**JIT Optimized** - Prevents CSS bundle bloat by intelligently routing arbitrary values to inline styles.
14+
-**JIT Conscious** - Designed for Tailwind JIT: utilities stay as class strings, while truly dynamic values can be expressed as inline styles.
1515
- 🔒 **Type-Safe** - Best-in-class TypeScript support with automatic prop inference.
1616
- 📦 **Minimal Overhead** - Ultra-lightweight runtime with only `clsx` and `tailwind-merge` as dependencies.
1717

@@ -82,28 +82,41 @@ button({ w: "w-full" });
8282

8383
### Interpolated Variants (Dynamic Props)
8484

85-
Interpolated variants provide a **Unified API** that bridges the gap between static Tailwind classes and dynamic inline styles. You can pass arbitrary values (handled as inline styles) or utility strings (handled as static classes) through a single prop, without breaking Tailwind's JIT compilation.
85+
Interpolated variants provide a **Unified API** that bridges static Tailwind classes and dynamic inline styles. A dynamic resolver can return either:
86+
87+
- a **Tailwind class string** (static utility), or
88+
- an object containing **className and/or style** (inline styles and optional utilities)
89+
90+
This is **JIT-friendly by design**, as long as the class strings you return are statically enumerable (i.e. appear in your source code).
91+
For truly unbounded values (e.g. pixel sizes), prefer returning style to avoid relying on arbitrary-value class generation.
8692

8793
```typescript
8894
const button = windCtrl({
8995
dynamic: {
90-
// ⚡ JIT Friendly Pattern:
91-
// Numbers -> Inline styles (bypassing JIT)
92-
// Strings -> Static classes (scanned by JIT)
96+
// Recommended pattern:
97+
// - Numbers -> inline styles (unbounded values)
98+
// - Strings -> Tailwind utilities (must be statically enumerable for JIT)
9399
w: (val) =>
94100
typeof val === "number" ? { style: { width: `${val}px` } } : val,
95101
},
96102
});
97103

98104
// Usage
99-
button({ w: "w-full" }); // -> className="w-full" (Static)
100-
button({ w: 200 }); // -> style="width: 200px" (Dynamic)
105+
button({ w: "w-full" }); // -> className includes "w-full" (static utility)
106+
button({ w: 200 }); // -> style includes { width: "200px" } (dynamic value)
101107
```
102108

109+
> **Note on Tailwind JIT**: Tailwind only generates CSS for class names it can statically detect in your source. Avoid constructing class strings dynamically (e.g. "`w-`" + `size`) unless you safelist them in your Tailwind config.
110+
103111
### Traits (Stackable States)
104112

105113
Traits are non-exclusive, stackable layers of state. Unlike `variants` (which are mutually exclusive), multiple traits can be active simultaneously. This declarative approach solves the "combinatorial explosion" problem often seen with `compoundVariants`.
106114

115+
Traits are **non-exclusive, stackable modifiers**. Unlike variants (mutually exclusive design choices), multiple traits can be active at the same time. This is a practical way to model boolean-like component states (e.g. `loading`, `disabled`, `glass`) without exploding compoundVariants.
116+
117+
When multiple traits generate conflicting utilities, Tailwind’s “last one wins” rule applies (via `tailwind-merge`).
118+
If ordering matters, prefer the **array form** to make precedence explicit.
119+
107120
```typescript
108121
const button = windCtrl({
109122
traits: {
@@ -113,10 +126,10 @@ const button = windCtrl({
113126
},
114127
});
115128

116-
// Usage - Array form (Clean & Type-safe)
129+
// Usage - Array form (explicit precedence; recommended when conflicts are possible)
117130
button({ traits: ["loading", "glass"] });
118131

119-
// Usage - Object form (Great for boolean props)
132+
// Usage - Object form (convenient for boolean props; order is not intended to be meaningful)
120133
button({ traits: { loading: isLoading, glass: true } });
121134
```
122135

@@ -169,6 +182,12 @@ const button = windCtrl({
169182
button({ intent: "primary", size: "lg" });
170183
```
171184

185+
## Gotchas
186+
187+
- **Tailwind JIT:** Tailwind only generates CSS for class names it can statically detect. Avoid constructing class strings dynamically unless you safelist them.
188+
- **Traits precedence:** If trait order matters, use the array form (`traits: ["a", "b"]`) to make precedence explicit.
189+
- **SSR/RSC:** Keep dynamic resolvers pure (same input → same output) to avoid hydration mismatches.
190+
172191
## License
173192

174193
The MIT License (MIT)

0 commit comments

Comments
 (0)