@@ -6,27 +6,27 @@ description: "Move recipes from tailwind-variants to class-recipe: tv becomes cv
66import CodeComparison from " ../../../components/CodeComparison.astro" ;
77
88A recipe of tailwind-variants without slots becomes a ` cva ` recipe, and
9- one with slots becomes an ` sva ` recipe. Most of the config moves over
10- with a few mechanical changes:
11-
12- | tailwind-variants | class-recipe |
13- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
14- | ` tv(config) ` | ` cva(config) ` , or ` sva(config) ` with slots |
15- | ` tv({ base }) ` , without variants | ` cva({ base, variants: {} }) ` , since ` variants ` [ is required] ( #a-recipe-needs-variants ) |
16- | Arrays of classes, including shared arrays spread into them | One string wherever the config takes classes; ` cx(...) ` turns an array into one |
17- | An option whose classes are ` null ` or ` false ` | An option whose classes are ` "" ` |
18- | ` { size: "sm", class: "..." } ` | ` { variants: { size: "sm" }, className: "..." } ` |
19- | ` class ` or ` className ` prop | ` className ` prop |
20- | ` slots: { base: "...", title: "..." } ` | ` slots: ["root", "title"] ` and ` base: { root: "...", title: "..." } ` |
21- | In a slot recipe, an option or a compound variant's ` class ` given as a string | The same classes under the slot that tailwind-variants names ` base ` , such as ` { root: "..." } ` |
22- | ` { size: "sm", class: { title: "..." } } ` | ` { variants: { size: "sm" }, classNames: { title: "..." } } ` |
23- | ` compoundSlots ` | A compound variant that gives the same classes to each slot in ` classNames ` , listed after the other compound variants |
24- | ` title({ class: "..." }) ` | ` card({ classNames: { title: "..." } }) ` |
25- | ` extend ` | A shared config object, spread into each recipe |
26- | ` VariantProps<typeof button> ` | ` VariantsOf<typeof button> ` , in which [ a required variant stays required] ( #a-variant-without-a-default-becomes-required ) |
27- | ` tv ` , ` cn ` , and ` cnMerge ` , which merge | ` cva ` , ` sva ` , and ` cx ` from ` createRecipes({ join: twMerge }) ` |
28- | ` createTV({ twMergeConfig }) ` | ` createRecipes ` with ` extendTailwindMerge ` , as in [ Merging classes] ( #merging-classes ) |
29- | ` tailwind-variants/lite ` , its ` cn(...)() ` , and ` cx ` , which do not merge | ` cva ` , ` sva ` , and ` cx ` from ` @lynstack/class-recipe ` |
9+ one with slots, or that extends a recipe with slots, becomes an ` sva `
10+ recipe. Most of the config moves over with a few mechanical changes:
11+
12+ | tailwind-variants | class-recipe |
13+ | -------------------------------------------------------------------------------------- | ------------------------------- ------------------------------------------------------------------------------------------------------------------------ |
14+ | ` tv(config) ` | ` cva(config) ` , or ` sva(config) ` with slots |
15+ | ` tv({ base }) ` , without variants | ` cva({ base, variants: {} }) ` , since ` variants ` [ is required] ( #a-recipe-needs-variants ) |
16+ | Arrays of classes, including shared arrays spread into them | One string wherever the config takes classes; ` cx(...) ` turns an array into one |
17+ | An option whose classes are ` null ` or ` false ` | An option whose classes are ` "" ` |
18+ | ` { size: "sm", class: "..." } ` , or ` className ` | ` { variants: { size: "sm" }, className: "..." } ` |
19+ | ` class ` or ` className ` prop | ` className ` prop, which takes one string and ignores any other value; ` cx(...) ` turns an array into one |
20+ | ` slots: { base: "...", title: "..." } ` | ` slots: ["root", "title"] ` and ` base: { root: "...", title: "..." } ` |
21+ | In a slot recipe, ` base ` , an option, or a compound variant's ` class ` given as a string | The same classes under the slot that tailwind-variants names ` base ` , such as ` { root: "..." } ` |
22+ | ` { size: "sm", class: { title: "..." } } ` | ` { variants: { size: "sm" }, classNames: { title: "..." } } ` |
23+ | ` compoundSlots ` | A compound variant that gives the same classes to each slot in ` classNames ` , listed after the other compound variants |
24+ | ` title({ class: "..." }) ` | ` card({ classNames: { title: "..." } }) ` |
25+ | ` extend ` | A shared config object, spread into each recipe |
26+ | ` VariantProps<typeof button> ` | ` VariantsOf<typeof button> ` , from ` @lynstack/class-recipe ` , in which [ a required variant stays required] ( #a-variant-without-a-default-becomes-required ) |
27+ | ` tv ` , ` cn(...) ` , and ` cnMerge(...)(config) ` , which merge | ` cva ` , ` sva ` , and ` cx(...) ` from ` createRecipes({ join: twMerge }) ` |
28+ | ` createTV({ twMergeConfig }) ` , or a ` tv ` of your own that passes one | ` createRecipes ` with ` extendTailwindMerge ` , as in [ Merging classes] ( #merging-classes ) |
29+ | ` tailwind-variants/lite ` , its ` cn(...)() ` , and ` cx ` , which do not merge | ` cva ` , ` sva ` , and ` cx ` from ` @lynstack/class-recipe ` |
3030
3131## Recipes
3232
4242
4343<CodeComparison name = " tailwind-variants/merged-classes" />
4444
45+ The merge also drops duplicate classes, and both builds of
46+ tailwind-variants collapse extra spaces. The default join keeps the
47+ classes as the config writes them, which changes snapshots of class
48+ names, but not which classes an element has.
49+
4550If you used ` tailwind-variants/lite ` , or ` twMerge: false ` everywhere,
4651nothing was merged: import ` cva ` , ` sva ` , and ` cx ` from
4752` @lynstack/class-recipe ` . You can also design your recipes so that no
@@ -61,15 +66,17 @@ export const { cx, cva, sva } = createRecipes({ join: twMerge });
6166
6267The ` cx ` it returns merges as ` cn ` does. For a recipe that passed
6368` { twMerge: false } ` or its own ` twMergeConfig ` to ` tv ` , create another
64- set of functions with ` createRecipes ` .
69+ set of functions with ` createRecipes ` , with the settings of both configs
70+ if your own ` tv ` combines them.
6571
6672If you pass a ` twMergeConfig ` , build the join with ` extendTailwindMerge `
6773from the same settings:
6874
6975- Give the names of the class groups that the config adds, and that
70- tailwind-merge does not define, as its type argument, as ` elevation `
71- below. A group that tailwind-merge defines, such as ` shadow ` , needs
72- none.
76+ tailwind-merge does not define, as its first type argument, as
77+ ` elevation ` below, and the names of the theme keys it adds as its
78+ second. A group or a key that tailwind-merge defines, such as ` shadow `
79+ or ` spacing ` , needs none.
7380- Move the settings of class groups that the config gives at its top
7481 level, such as ` classGroups ` or ` theme ` , under ` extend ` .
7582- Leave out ` prefix ` : tailwind-variants 3 ignores it, and tailwind-merge
@@ -106,13 +113,20 @@ the `base` property.
106113
107114A slot function of tailwind-variants can also take variants of its own,
108115as in ` title({ size: "md" }) ` , which override those of the call. Call the
109- slot recipe again with those variants instead.
116+ slot recipe again with both instead: ` card({ ...variants, size: "md" }).title ` .
117+ A component that receives the slots without the variants, such as one
118+ that reads them from a context, joins its own classes with ` cx ` :
119+ ` cx(slots.title, className) ` gives the classes that ` classNames ` gives.
120+ tailwind-variants ignores ` class ` and ` className ` passed to the slot
121+ recipe itself, rather than to a slot, so drop them.
110122
111123tailwind-variants also returns a ` base ` slot that the config does not
112- declare. Its function returns ` undefined ` unless an option or a compound
113- variant gives classes as a string. A slot recipe returns only the slots
114- that ` slots ` lists, so give such classes to a slot you declare, and drop
115- a ` base ` that you read from such a recipe.
124+ declare. It holds the classes of the config's top-level ` base ` , unless
125+ ` slots.base ` replaces them, and of each option or compound variant that
126+ gives classes as a string; without them, its function returns
127+ ` undefined ` . A slot recipe returns only the slots that ` slots ` lists, so
128+ give such classes to a slot you declare, and drop a ` base ` that you read
129+ from such a recipe.
116130
117131A compound slot becomes a compound variant that gives the same classes to
118132each of its slots. tailwind-variants adds the classes of compound slots
@@ -135,9 +149,16 @@ classes of an option given again to those of the recipe it extends,
135149merges ` defaultVariants ` and ` slots ` , and concatenates ` compoundVariants `
136150and ` compoundSlots ` . It also lists the variants of the new recipe before
137151those it extends, which decides the order of their classes. A spread
138- replaces each property it gives again, so join ` base ` yourself, repeat
139- the classes of an option you keep, spread the shared ` compoundVariants `
140- into the new array, and declare the new variants first, as above.
152+ replaces each property it gives again, so:
153+
154+ - Join ` base ` yourself, the classes of the recipe it extends first.
155+ - Spread the shared ` compoundVariants ` first into the new array.
156+ - Declare the new variants before the shared ones, as above.
157+ - Take a variant that both recipes declare out of the shared variants,
158+ which would replace it, and declare it with the new ones, joining the
159+ classes of each option that both give:
160+
161+ <CodeComparison name = " tailwind-variants/extend-variant" />
141162
142163A slot recipe extends the same way. Spread its ` slots ` into the new list,
143164and join the ` base ` classes of each slot that the new recipe gives again.
@@ -161,12 +182,17 @@ omitted, as tailwind-variants does, default to an option without classes:
161182
162183<CodeComparison name = " tailwind-variants/required-variant" />
163184
164- The new option becomes a value that the props of a component accept.
165- Give it a name that the variant does not use yet, such as ` none ` or
166- ` unset ` ; in a slot recipe, its classes are ` {} ` . If every caller already
167- passes the variant, as a component does that gives the prop a default
168- when it destructures it, make that value the default of the recipe
169- instead, and the component needs no default of its own.
185+ Give the new option a name that the variant does not use yet, such as
186+ ` none ` or ` unset ` ; it becomes a value that the props of a component
187+ accept. In a slot recipe, its classes are ` {} ` . An option without classes
188+ that the variant declares already works too, unless a compound variant
189+ names it, since the condition would then match calls that omit the
190+ variant.
191+
192+ If every caller already passes the variant, such as a component that
193+ destructures the prop with a default, make that value the default of the
194+ recipe instead. Keep the component's default as well if it reads the
195+ prop for anything else, such as a ` data-variant ` attribute.
170196
171197### Only a boolean variant falls back to its ` false ` option
172198
@@ -178,7 +204,8 @@ options is required instead, so make `"false"` its default:
178204
179205### A recipe without classes returns ` "" `
180206
181- tailwind-variants returns ` undefined ` when a call adds no classes:
207+ tailwind-variants returns ` undefined ` when a call adds no classes, and so
208+ do a slot without classes, ` cn ` , and ` cx ` :
182209
183210<CodeComparison name = " tailwind-variants/empty-result" />
184211
@@ -192,6 +219,10 @@ no classes, and the condition matches as before:
192219
193220<CodeComparison name = " tailwind-variants/undeclared-variant" />
194221
222+ For a prop that is not boolean, declare an option for each value that a
223+ condition names, and one more as its default, such as ` none ` , so that
224+ calls without the prop stay valid.
225+
195226### A recipe needs ` variants `
196227
197228A recipe without them is a type error and throws a ` TypeError ` when it
@@ -201,7 +232,8 @@ is created. Pass `variants: {}`.
201232
202233tailwind-variants also exposes the config of a recipe, such as
203234` button.variants ` and ` button.slots ` . Keep the config in an object if you
204- read it.
235+ read it. ` variantKeys ` is a readonly array, so a function that takes it
236+ as ` K[] ` needs ` readonly K[] ` .
205237
206238## Behaviors that change with untyped data
207239
@@ -217,6 +249,13 @@ one with a default:
217249
218250<CodeComparison name = " tailwind-variants/null-variant" />
219251
252+ ### An empty string adds no classes for the variant
253+
254+ tailwind-variants uses the variant's ` false ` option instead, even when
255+ the variant has a default:
256+
257+ <CodeComparison name = " tailwind-variants/empty-string" />
258+
220259### A compound condition on ` undefined ` matches any option
221260
222261A condition on ` undefined ` matches any option, as a variant left out of
@@ -226,6 +265,11 @@ or a default of `false`:
226265
227266<CodeComparison name = " tailwind-variants/undefined-condition" />
228267
268+ Name the option instead: the option that a variant without a default now
269+ defaults to, such as ` none ` or ` false ` , or ` false ` for a variant whose
270+ default is ` false ` . For a variant with another default, the condition
271+ matched only untyped values, so delete the compound variant.
272+
229273### A compound condition matches an option by its name
230274
231275A condition on ` true ` matches the prop ` "true" ` , and one on ` 2 ` matches
0 commit comments