classname-variants 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -1
- package/lib/example/preact.d.ts +1 -1
- package/lib/example/preact.js +11 -6
- package/lib/example/react.d.ts +1 -1
- package/lib/example/react.js +11 -6
- package/lib/index.d.ts +3 -0
- package/lib/index.js +7 -4
- package/lib/preact.d.ts +2 -10
- package/lib/preact.js +2 -4
- package/lib/react.d.ts +2 -10
- package/lib/react.js +2 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
Library to create type-safe components that render their class name based on a set of variants.
|
|
4
4
|
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ⚛️ Supports React, Preact and vanilla DOM
|
|
8
|
+
- 🛡️ Fully type-safe and excellent auto completion support
|
|
9
|
+
- ✅ Supports both optional and required variants
|
|
10
|
+
- 🔗 Supports custom strategies like [tailwind-merge](https://www.npmjs.com/package/tailwind-merge)
|
|
11
|
+
- 🪶 [Light-weight](https://bundlephobia.com/package/classname-variants) without any dependencies
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
5
15
|
# Examples
|
|
6
16
|
|
|
7
17
|
Here is an example that uses React and Tailwind CSS:
|
|
@@ -138,7 +148,7 @@ The `compoundVariants` option can be used to apply class names based on a combin
|
|
|
138
148
|
|
|
139
149
|
### Default variants
|
|
140
150
|
|
|
141
|
-
You can use the `defaultVariants` property to
|
|
151
|
+
If you define a variant it becomes a required prop unless you specify a default (or the variant is boolean). You can use the `defaultVariants` property to specify defaults:
|
|
142
152
|
|
|
143
153
|
```ts
|
|
144
154
|
{
|
|
@@ -223,6 +233,23 @@ The component can then be rendered as button or as anchor or even as custom comp
|
|
|
223
233
|
</>
|
|
224
234
|
```
|
|
225
235
|
|
|
236
|
+
### Using a custom strategy to combine class names
|
|
237
|
+
|
|
238
|
+
The built-in strategy for combining multiple class names into one string is simple and straightforward:
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
(classes) => classes.filter(Boolean).join(" ");
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
If you [want](https://github.com/dcastil/tailwind-merge/blob/main/docs/when-and-how-to-use-it.md), you can use a custom strategy like tailwind-merge instead:
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
import { classNames } from "classname-variants";
|
|
248
|
+
import { twMerge } from "tailwind-merge";
|
|
249
|
+
|
|
250
|
+
classNames.combine = twMerge;
|
|
251
|
+
```
|
|
252
|
+
|
|
226
253
|
# Tailwind IntelliSense
|
|
227
254
|
|
|
228
255
|
In order to get auto-completion for the CSS classes themselves, you can use the [Tailwind CSS IntelliSense](https://github.com/tailwindlabs/tailwindcss-intellisense) plugin for VS Code. In order to make it recognize the strings inside your variants-config, you have to somehow mark them and configure the plugin accordingly.
|
package/lib/example/preact.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { h } from "preact";
|
|
3
3
|
export declare const ExpectErrors: <As extends keyof h.JSX.IntrinsicElements | import("preact").ComponentType<any> = "div">(props: {
|
|
4
4
|
as?: As | undefined;
|
|
5
|
-
} & Omit<import("preact").ComponentProps<As>, "as"> & {
|
|
5
|
+
} & Omit<import("preact").ComponentProps<As>, "as" | "color"> & {
|
|
6
6
|
color: "neutral" | "accent";
|
|
7
7
|
} & {}) => import("preact").VNode<{}> | null;
|
|
8
8
|
export declare function WithErrors(): h.JSX.Element;
|
package/lib/example/preact.js
CHANGED
|
@@ -13,6 +13,10 @@ const Button = styled("button", {
|
|
|
13
13
|
neutral: "bg-slate-500 hover:bg-slate-400",
|
|
14
14
|
accent: "bg-teal-500 hover:bg-teal-400",
|
|
15
15
|
},
|
|
16
|
+
size: {
|
|
17
|
+
small: "text-sm",
|
|
18
|
+
medium: "text-md",
|
|
19
|
+
},
|
|
16
20
|
outlined: {
|
|
17
21
|
true: "border-2",
|
|
18
22
|
},
|
|
@@ -52,16 +56,17 @@ export const ExpectErrors = styled("div", {
|
|
|
52
56
|
});
|
|
53
57
|
export function WithErrors() {
|
|
54
58
|
return (h("div", null,
|
|
55
|
-
h(Button, { foo: true }, "unknown property"),
|
|
59
|
+
h(Button, { foo: true, size: "medium" }, "unknown property"),
|
|
56
60
|
h(Card, { foo: true }, "Unknown property"),
|
|
57
|
-
h(Button, { color: "foo" }, "Invalid variant")
|
|
61
|
+
h(Button, { size: "medium", color: "foo" }, "Invalid variant"),
|
|
62
|
+
h(Button, null, "Missing size")));
|
|
58
63
|
}
|
|
59
64
|
export function PreactApp() {
|
|
60
65
|
return (h("div", { className: "flex justify-center items-center pt-8 gap-4 flex-wrap" },
|
|
61
|
-
h(Button, { onClick: console.log }, "Accent"),
|
|
62
|
-
h(Button, { rounded: true }, "Neutral + Rounded"),
|
|
63
|
-
h(Button, { color: "accent", outlined: true }, "Accent + Outlined"),
|
|
64
|
-
h(Button, { color: "accent", disabled: true }, "Disabled"),
|
|
66
|
+
h(Button, { size: "medium", onClick: console.log }, "Accent"),
|
|
67
|
+
h(Button, { size: "medium", rounded: true }, "Neutral + Rounded"),
|
|
68
|
+
h(Button, { size: "medium", color: "accent", outlined: true }, "Accent + Outlined"),
|
|
69
|
+
h(Button, { size: "medium", color: "accent", disabled: true }, "Disabled"),
|
|
65
70
|
h(TitleCard, { title: "Hello" }),
|
|
66
71
|
h(Card, null,
|
|
67
72
|
h("h1", null, "Hello"),
|
package/lib/example/react.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
export declare const ExpectErrors: <As extends React.ElementType<any> = "div">(props: {
|
|
3
3
|
as?: As | undefined;
|
|
4
|
-
} & Omit<React.ComponentProps<As>, "as"> & {
|
|
4
|
+
} & Omit<React.ComponentProps<As>, "as" | "color"> & {
|
|
5
5
|
color: "neutral" | "accent";
|
|
6
6
|
} & {}) => React.ReactElement<any, string | React.JSXElementConstructor<any>> | null;
|
|
7
7
|
export declare function WithErrors(): JSX.Element;
|
package/lib/example/react.js
CHANGED
|
@@ -12,6 +12,10 @@ const Button = styled("button", {
|
|
|
12
12
|
neutral: "bg-slate-500 hover:bg-slate-400",
|
|
13
13
|
accent: "bg-teal-500 hover:bg-teal-400",
|
|
14
14
|
},
|
|
15
|
+
size: {
|
|
16
|
+
small: "text-sm",
|
|
17
|
+
medium: "text-md",
|
|
18
|
+
},
|
|
15
19
|
outlined: {
|
|
16
20
|
true: "border-2",
|
|
17
21
|
},
|
|
@@ -51,17 +55,18 @@ export const ExpectErrors = styled("div", {
|
|
|
51
55
|
});
|
|
52
56
|
export function WithErrors() {
|
|
53
57
|
return (React.createElement("div", null,
|
|
54
|
-
React.createElement(Button, { foo: true }, "unknown property"),
|
|
58
|
+
React.createElement(Button, { foo: true, size: "medium" }, "unknown property"),
|
|
55
59
|
React.createElement(Card, { foo: true }, "Unknown property"),
|
|
56
|
-
React.createElement(Button, { color: "foo" }, "Invalid variant"),
|
|
60
|
+
React.createElement(Button, { size: "medium", color: "foo" }, "Invalid variant"),
|
|
61
|
+
React.createElement(Button, null, "Missing size"),
|
|
57
62
|
React.createElement(Card, { as: "b", href: "https://example.com" }, "B tags don't have a href attribute")));
|
|
58
63
|
}
|
|
59
64
|
export function ReactApp() {
|
|
60
65
|
return (React.createElement("div", { className: "flex justify-center items-center pt-8 gap-4 flex-wrap" },
|
|
61
|
-
React.createElement(Button, { onClick: console.log }, "Accent"),
|
|
62
|
-
React.createElement(Button, { rounded: true }, "Neutral + Rounded"),
|
|
63
|
-
React.createElement(Button, { color: "accent", outlined: true }, "Accent + Outlined"),
|
|
64
|
-
React.createElement(Button, { color: "accent", disabled: true }, "Disabled"),
|
|
66
|
+
React.createElement(Button, { size: "medium", onClick: console.log }, "Accent"),
|
|
67
|
+
React.createElement(Button, { size: "medium", rounded: true }, "Neutral + Rounded"),
|
|
68
|
+
React.createElement(Button, { size: "medium", color: "accent", outlined: true }, "Accent + Outlined"),
|
|
69
|
+
React.createElement(Button, { size: "medium", color: "accent", disabled: true }, "Disabled"),
|
|
65
70
|
React.createElement(TitleCard, { title: "Hello" }),
|
|
66
71
|
React.createElement(Card, null,
|
|
67
72
|
React.createElement("h1", null, "Hello"),
|
package/lib/index.d.ts
CHANGED
|
@@ -101,6 +101,9 @@ export type VariantOptions<C extends VariantsConfig<V>, V extends Variants = C["
|
|
|
101
101
|
export type Simplify<T> = {
|
|
102
102
|
[K in keyof T]: T[K];
|
|
103
103
|
};
|
|
104
|
+
export declare const classNames: {
|
|
105
|
+
combine: (...classes: Array<string | undefined | null | false | 0>) => string;
|
|
106
|
+
};
|
|
104
107
|
export declare function variants<C extends VariantsConfig<V>, V extends Variants = C["variants"]>(config: Simplify<C>): (props: VariantOptions<C>) => string;
|
|
105
108
|
/**
|
|
106
109
|
* No-op function to mark template literals as tailwind strings.
|
package/lib/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export const classNames = {
|
|
2
|
+
combine: (...classes) => classes.filter(Boolean).join(" "),
|
|
3
|
+
};
|
|
1
4
|
export function variants(config) {
|
|
2
5
|
const { base, variants, compoundVariants, defaultVariants } = config;
|
|
3
6
|
const isBooleanVariant = (name) => {
|
|
@@ -6,7 +9,7 @@ export function variants(config) {
|
|
|
6
9
|
};
|
|
7
10
|
return (props) => {
|
|
8
11
|
var _a;
|
|
9
|
-
const
|
|
12
|
+
const classes = base ? [base] : [];
|
|
10
13
|
const getSelected = (name) => {
|
|
11
14
|
var _a, _b;
|
|
12
15
|
return (_b = (_a = props[name]) !== null && _a !== void 0 ? _a : defaultVariants === null || defaultVariants === void 0 ? void 0 : defaultVariants[name]) !== null && _b !== void 0 ? _b : (isBooleanVariant(name) ? false : undefined);
|
|
@@ -14,15 +17,15 @@ export function variants(config) {
|
|
|
14
17
|
for (let name in variants) {
|
|
15
18
|
const selected = getSelected(name);
|
|
16
19
|
if (selected !== undefined)
|
|
17
|
-
|
|
20
|
+
classes.push((_a = variants[name]) === null || _a === void 0 ? void 0 : _a[selected]);
|
|
18
21
|
}
|
|
19
22
|
for (let { variants, className } of compoundVariants !== null && compoundVariants !== void 0 ? compoundVariants : []) {
|
|
20
23
|
const isSelected = (name) => getSelected(name) === variants[name];
|
|
21
24
|
if (Object.keys(variants).every(isSelected)) {
|
|
22
|
-
|
|
25
|
+
classes.push(className);
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
|
-
return
|
|
28
|
+
return classNames.combine(...classes);
|
|
26
29
|
};
|
|
27
30
|
}
|
|
28
31
|
/**
|
package/lib/preact.d.ts
CHANGED
|
@@ -19,16 +19,8 @@ type VariantsOf<T, V> = T extends VariantsConfig ? V : {};
|
|
|
19
19
|
type AsProps<T extends ElementType = ElementType> = {
|
|
20
20
|
as?: T;
|
|
21
21
|
};
|
|
22
|
-
type PolymorphicComponentProps<T extends ElementType> = AsProps<T> & Omit<ComponentProps<T>, "as"
|
|
23
|
-
export declare function styled<T extends ElementType, C extends VariantsConfig<V>, V extends Variants = VariantsOf<C, C["variants"]>>(type: T, config: string | Simplify<C>): <As extends ElementType<any> = T>(props:
|
|
24
|
-
true: any;
|
|
25
|
-
} | {
|
|
26
|
-
false: any;
|
|
27
|
-
} ? K_1 : never]: C["variants"][K_1]; } : never) | keyof (C["variants"] extends infer T_3 extends Variants ? { [K_2 in keyof T_3 as K_2 extends keyof C["defaultVariants"] ? K_2 : never]: C["variants"][K_2]; } : never) ? never : K]: (C["variants"] extends infer T_4 extends Variants ? { [K_3 in keyof T_4]: keyof C["variants"][K_3] extends "true" | "false" ? boolean : keyof C["variants"][K_3]; } : never)[K]; } : never) & (C["variants"] extends infer T_5 extends Variants ? { [K_4 in keyof T_5 as K_4 extends keyof (C["variants"] extends infer T_2 extends Variants ? { [K_1 in keyof T_2 as C["variants"][K_1] extends {
|
|
28
|
-
true: any;
|
|
29
|
-
} | {
|
|
30
|
-
false: any;
|
|
31
|
-
} ? K_1 : never]: C["variants"][K_1]; } : never) | keyof (C["variants"] extends infer T_3 extends Variants ? { [K_2 in keyof T_3 as K_2 extends keyof C["defaultVariants"] ? K_2 : never]: C["variants"][K_2]; } : never) ? K_4 : never]?: (C["variants"] extends infer T_4 extends Variants ? { [K_3 in keyof T_4]: keyof C["variants"][K_3] extends "true" | "false" ? boolean : keyof C["variants"][K_3]; } : never)[K_4] | undefined; } : never)) => VNode | null;
|
|
22
|
+
type PolymorphicComponentProps<V, T extends ElementType> = AsProps<T> & Omit<ComponentProps<T>, "as" | keyof V> & V;
|
|
23
|
+
export declare function styled<T extends ElementType, C extends VariantsConfig<V>, V extends Variants = VariantsOf<C, C["variants"]>>(type: T, config: string | Simplify<C>): <As extends ElementType<any> = T>(props: PolymorphicComponentProps<VariantOptions<C, C["variants"]>, As>) => VNode | null;
|
|
32
24
|
/**
|
|
33
25
|
* No-op function to mark template literals as tailwind strings.
|
|
34
26
|
*/
|
package/lib/preact.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { h } from "preact";
|
|
2
2
|
import { forwardRef } from "preact/compat";
|
|
3
|
-
import { variants, } from "./index.js";
|
|
3
|
+
import { variants, classNames, } from "./index.js";
|
|
4
4
|
export function variantProps(config) {
|
|
5
5
|
const variantClassName = variants(config);
|
|
6
6
|
return (props) => {
|
|
@@ -12,9 +12,7 @@ export function variantProps(config) {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
// Add the optionally passed class/className prop for chaining
|
|
15
|
-
result.class =
|
|
16
|
-
.filter(Boolean)
|
|
17
|
-
.join(" ");
|
|
15
|
+
result.class = classNames.combine(variantClassName(props), props.class, props.className);
|
|
18
16
|
return result;
|
|
19
17
|
};
|
|
20
18
|
}
|
package/lib/react.d.ts
CHANGED
|
@@ -17,16 +17,8 @@ type VariantsOf<T, V> = T extends VariantsConfig ? V : {};
|
|
|
17
17
|
type AsProps<T extends ElementType = ElementType> = {
|
|
18
18
|
as?: T;
|
|
19
19
|
};
|
|
20
|
-
type PolymorphicComponentProps<T extends ElementType> = AsProps<T> & Omit<ComponentProps<T>, "as"
|
|
21
|
-
export declare function styled<T extends ElementType, C extends VariantsConfig<V>, V extends Variants = VariantsOf<C, C["variants"]>>(type: T, config: string | Simplify<C>): <As extends ElementType<any> = T>(props:
|
|
22
|
-
true: any;
|
|
23
|
-
} | {
|
|
24
|
-
false: any;
|
|
25
|
-
} ? K_1 : never]: C["variants"][K_1]; } : never) | keyof (C["variants"] extends infer T_3 extends Variants ? { [K_2 in keyof T_3 as K_2 extends keyof C["defaultVariants"] ? K_2 : never]: C["variants"][K_2]; } : never) ? never : K]: (C["variants"] extends infer T_4 extends Variants ? { [K_3 in keyof T_4]: keyof C["variants"][K_3] extends "true" | "false" ? boolean : keyof C["variants"][K_3]; } : never)[K]; } : never) & (C["variants"] extends infer T_5 extends Variants ? { [K_4 in keyof T_5 as K_4 extends keyof (C["variants"] extends infer T_2 extends Variants ? { [K_1 in keyof T_2 as C["variants"][K_1] extends {
|
|
26
|
-
true: any;
|
|
27
|
-
} | {
|
|
28
|
-
false: any;
|
|
29
|
-
} ? K_1 : never]: C["variants"][K_1]; } : never) | keyof (C["variants"] extends infer T_3 extends Variants ? { [K_2 in keyof T_3 as K_2 extends keyof C["defaultVariants"] ? K_2 : never]: C["variants"][K_2]; } : never) ? K_4 : never]?: (C["variants"] extends infer T_4 extends Variants ? { [K_3 in keyof T_4]: keyof C["variants"][K_3] extends "true" | "false" ? boolean : keyof C["variants"][K_3]; } : never)[K_4] | undefined; } : never)) => ReactElement | null;
|
|
20
|
+
type PolymorphicComponentProps<V, T extends ElementType> = AsProps<T> & Omit<ComponentProps<T>, "as" | keyof V> & V;
|
|
21
|
+
export declare function styled<T extends ElementType, C extends VariantsConfig<V>, V extends Variants = VariantsOf<C, C["variants"]>>(type: T, config: string | Simplify<C>): <As extends ElementType<any> = T>(props: PolymorphicComponentProps<VariantOptions<C, C["variants"]>, As>) => ReactElement | null;
|
|
30
22
|
/**
|
|
31
23
|
* No-op function to mark template literals as tailwind strings.
|
|
32
24
|
*/
|
package/lib/react.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createElement, forwardRef, } from "react";
|
|
2
|
-
import { variants, } from "./index.js";
|
|
2
|
+
import { variants, classNames, } from "./index.js";
|
|
3
3
|
export function variantProps(config) {
|
|
4
4
|
const variantClassName = variants(config);
|
|
5
5
|
return (props) => {
|
|
@@ -11,9 +11,7 @@ export function variantProps(config) {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
// Add the optionally passed className prop for chaining
|
|
14
|
-
result.className =
|
|
15
|
-
.filter(Boolean)
|
|
16
|
-
.join(" ");
|
|
14
|
+
result.className = classNames.combine(variantClassName(props), props.className);
|
|
17
15
|
return result;
|
|
18
16
|
};
|
|
19
17
|
}
|