classname-variants 1.3.2 → 1.4.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 +131 -136
- package/index.html +5 -2
- package/lib/example/index.d.ts +1 -0
- package/lib/example/index.js +8 -0
- package/lib/example/preact.d.ts +9 -0
- package/lib/example/preact.js +71 -0
- package/lib/example/react.d.ts +8 -0
- package/lib/example/react.js +71 -0
- package/lib/preact.d.ts +38 -0
- package/lib/preact.js +33 -0
- package/lib/react.js +1 -1
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -1,20 +1,58 @@
|
|
|
1
|
-
# classname-variants
|
|
1
|
+
# classname-variants 🌈
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Library to create type-safe components that render their class name based on a set of variants.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# Examples
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Here is an example that uses React and Tailwind CSS:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
```tsx
|
|
10
|
+
import { styled } from "classname-variants/react";
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
const Button = styled("button", {
|
|
13
|
+
variants: {
|
|
14
|
+
size: {
|
|
15
|
+
small: "text-xs",
|
|
16
|
+
large: "text-lg",
|
|
17
|
+
},
|
|
18
|
+
primary: {
|
|
19
|
+
true: "bg-teal-500 text-white",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function UsageExample() {
|
|
25
|
+
return <Button primary size="large" />;
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
[](https://codesandbox.io/s/classname-variants-react-3bzjl?fontsize=14&hidenavigation=1&theme=dark)
|
|
12
30
|
|
|
13
|
-
|
|
31
|
+
While the library has been designed with tools like Tailwind in mind, it can be also used with custom classes or CSS modules:
|
|
14
32
|
|
|
15
|
-
|
|
33
|
+
## Preact + CSS modules
|
|
16
34
|
|
|
17
35
|
```tsx
|
|
36
|
+
import { styled } from "classname-variants/preact";
|
|
37
|
+
import styles from "./styles.module.css";
|
|
38
|
+
|
|
39
|
+
const Button = styled("button", {
|
|
40
|
+
variants: {
|
|
41
|
+
size: {
|
|
42
|
+
small: styles.small,
|
|
43
|
+
large: styles.large,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Vanilla DOM
|
|
50
|
+
|
|
51
|
+
The core of the library is completely framework-agnostic:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { variants } from "classname-variants";
|
|
55
|
+
|
|
18
56
|
const button = variants({
|
|
19
57
|
base: "rounded text-white",
|
|
20
58
|
variants: {
|
|
@@ -22,50 +60,61 @@ const button = variants({
|
|
|
22
60
|
brand: "bg-sky-500",
|
|
23
61
|
accent: "bg-teal-500",
|
|
24
62
|
},
|
|
25
|
-
size: {
|
|
26
|
-
small: "px-5 py-3 text-xs",
|
|
27
|
-
large: "px-6 py-4 text-base",
|
|
28
|
-
},
|
|
29
63
|
},
|
|
30
64
|
});
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
The result is a function that expects an object which specifies what variants should be selected. When called, it returns a string containing the respective class names:
|
|
34
65
|
|
|
35
|
-
```ts
|
|
36
66
|
document.write(`
|
|
37
|
-
<button class="${button({
|
|
38
|
-
color: "accent",
|
|
39
|
-
size: "large",
|
|
40
|
-
})}">
|
|
67
|
+
<button class="${button({ color: "accent" })}">
|
|
41
68
|
Click Me!
|
|
42
69
|
</button>
|
|
43
70
|
`);
|
|
44
71
|
```
|
|
45
72
|
|
|
46
|
-
#
|
|
73
|
+
# API
|
|
47
74
|
|
|
48
|
-
|
|
75
|
+
### Defining variants
|
|
49
76
|
|
|
50
|
-
|
|
77
|
+
You can add any number of variants by using the `variants` key.
|
|
51
78
|
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
base: "text-white",
|
|
79
|
+
```ts
|
|
80
|
+
{
|
|
55
81
|
variants: {
|
|
56
|
-
|
|
57
|
-
|
|
82
|
+
color: {
|
|
83
|
+
primary: "bg-teal",
|
|
84
|
+
secondary: "bg-indigo",
|
|
85
|
+
danger: "bg-red"
|
|
86
|
+
},
|
|
87
|
+
size: {
|
|
88
|
+
small: "text-sm",
|
|
89
|
+
medium: "text-md",
|
|
90
|
+
large: "text-lg",
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Boolean variants
|
|
97
|
+
|
|
98
|
+
Variants can be typed as `boolean` by using `true` / `false` as key:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
{
|
|
102
|
+
variants: {
|
|
103
|
+
primary: {
|
|
104
|
+
true: "bg-teal-500",
|
|
58
105
|
},
|
|
59
106
|
},
|
|
60
|
-
}
|
|
107
|
+
}
|
|
61
108
|
```
|
|
62
109
|
|
|
63
|
-
|
|
110
|
+
```ts
|
|
111
|
+
<Button primary>Click Me!</Button>
|
|
112
|
+
```
|
|
64
113
|
|
|
65
|
-
The `compoundVariants` option can be used to apply class names based on a combination of other variants
|
|
114
|
+
The `compoundVariants` option can be used to apply class names based on a combination of other variants:
|
|
66
115
|
|
|
67
|
-
```
|
|
68
|
-
|
|
116
|
+
```ts
|
|
117
|
+
{
|
|
69
118
|
variants: {
|
|
70
119
|
color: {
|
|
71
120
|
neutral: "bg-gray-200",
|
|
@@ -84,128 +133,71 @@ const button = variants({
|
|
|
84
133
|
className: "border-teal-500",
|
|
85
134
|
},
|
|
86
135
|
],
|
|
87
|
-
}
|
|
136
|
+
}
|
|
88
137
|
```
|
|
89
138
|
|
|
90
|
-
|
|
139
|
+
### Default variants
|
|
91
140
|
|
|
92
|
-
|
|
141
|
+
You can use the `defaultVariants` property to set defaults:
|
|
93
142
|
|
|
94
143
|
```ts
|
|
95
|
-
|
|
144
|
+
{
|
|
96
145
|
variants: {
|
|
97
146
|
color: {
|
|
98
|
-
|
|
99
|
-
|
|
147
|
+
primary: "bg-teal-300",
|
|
148
|
+
secondary: "bg-teal-100"
|
|
100
149
|
},
|
|
101
150
|
},
|
|
102
151
|
defaultVariants: {
|
|
103
|
-
color: "
|
|
104
|
-
}
|
|
105
|
-
}
|
|
152
|
+
color: "secondary",
|
|
153
|
+
}
|
|
154
|
+
}
|
|
106
155
|
```
|
|
107
156
|
|
|
108
|
-
|
|
157
|
+
### Base class
|
|
109
158
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
It works much like `variants()` but instead of a class name string, the resulting function returns an object with props.
|
|
159
|
+
Use the `base` property to specify class names that should always be applied:
|
|
113
160
|
|
|
114
161
|
```ts
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const buttonProps = variantProps({
|
|
118
|
-
base: "rounded-md text-white",
|
|
162
|
+
{
|
|
163
|
+
base: "text-black rounded-full px-2",
|
|
119
164
|
variants: {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
accent: "bg-teal-500",
|
|
123
|
-
},
|
|
124
|
-
size: {
|
|
125
|
-
small: "px-5 py-3 text-xs",
|
|
126
|
-
large: "px-6 py-4 text-base",
|
|
127
|
-
},
|
|
128
|
-
rounded: {
|
|
129
|
-
true: "rounded-full",
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
defaultVariants: {
|
|
133
|
-
color: "brand",
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
This way a component's props (or part of them) can be directly spread into the target element. All variant-related props are used to construct the `className` property while all other props are passed through verbatim:
|
|
139
|
-
|
|
140
|
-
```tsx
|
|
141
|
-
type Props = JSX.IntrinsicElements["button"] &
|
|
142
|
-
VariantPropsOf<typeof buttonProps>;
|
|
143
|
-
|
|
144
|
-
function Button(props: Props) {
|
|
145
|
-
return <button {...buttonProps(props)} />;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function App() {
|
|
149
|
-
return (
|
|
150
|
-
<Button size="small" color="accent" onClick={console.log}>
|
|
151
|
-
Click Me!
|
|
152
|
-
</Button>
|
|
153
|
-
);
|
|
165
|
+
// ...
|
|
166
|
+
}
|
|
154
167
|
}
|
|
155
168
|
```
|
|
156
169
|
|
|
157
|
-
|
|
170
|
+
### Components without variants
|
|
158
171
|
|
|
159
|
-
|
|
172
|
+
Sometimes it can be useful to define styled components that
|
|
173
|
+
don't have any variants, which can be done like this:
|
|
160
174
|
|
|
161
|
-
```
|
|
162
|
-
import { styled
|
|
175
|
+
```tsx
|
|
176
|
+
import { styled } from "classname-variants/react";
|
|
163
177
|
|
|
164
|
-
const Button = styled("button",
|
|
165
|
-
variants: {
|
|
166
|
-
size: {
|
|
167
|
-
small: tw`text-xs`,
|
|
168
|
-
large: tw`text-base`,
|
|
169
|
-
},
|
|
170
|
-
},
|
|
171
|
-
});
|
|
178
|
+
const Button = styled("button", "bg-transparent border p-2");
|
|
172
179
|
```
|
|
173
180
|
|
|
174
|
-
|
|
181
|
+
### Styling custom components
|
|
175
182
|
|
|
176
|
-
|
|
177
|
-
import { styled } from "classname-variants/react";
|
|
178
|
-
import styles from "./styles.module.css";
|
|
183
|
+
You can style any custom React/Preact component as long as they accept a `className` prop (or `class` in case of Preact).
|
|
179
184
|
|
|
180
|
-
|
|
185
|
+
```tsx
|
|
186
|
+
function MyComponent(props) {
|
|
187
|
+
return <div {...props}>I'm a stylable custom component.</div>;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const MyStyledComponent = styled(MyComponent, {
|
|
191
|
+
base: "some-class",
|
|
181
192
|
variants: {
|
|
182
|
-
|
|
183
|
-
small: styles.small,
|
|
184
|
-
large: styles.large,
|
|
185
|
-
},
|
|
193
|
+
// ...
|
|
186
194
|
},
|
|
187
195
|
});
|
|
188
196
|
```
|
|
189
197
|
|
|
190
|
-
|
|
191
|
-
> You can also style other custom React components as long as they accept a `className` prop.
|
|
192
|
-
|
|
193
|
-
## Styled components without variants
|
|
194
|
-
|
|
195
|
-
You can also use the `styled` function to create styled components without any variants at all:
|
|
196
|
-
|
|
197
|
-
```ts
|
|
198
|
-
import { styled } from "classname-variants/react";
|
|
199
|
-
|
|
200
|
-
const Button = styled(
|
|
201
|
-
"button",
|
|
202
|
-
"border-none rounded px-3 font-sans bg-green-600 text-white hover:bg-green-500"
|
|
203
|
-
);
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
## Polymorphic components with "as"
|
|
198
|
+
### Polymorphic components with "as"
|
|
207
199
|
|
|
208
|
-
If you want to keep all the variants you have defined for a component but want to render a different HTML tag or a different custom component, you can use the
|
|
200
|
+
If you want to keep all the variants you have defined for a component but want to render a different HTML tag or a different custom component, you can use the `as` prop to do so:
|
|
209
201
|
|
|
210
202
|
```tsx
|
|
211
203
|
import { styled } from "classname-variants/react";
|
|
@@ -215,17 +207,20 @@ const Button = styled("button", {
|
|
|
215
207
|
//...
|
|
216
208
|
},
|
|
217
209
|
});
|
|
210
|
+
```
|
|
218
211
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
212
|
+
The component can then be rendered as button or as anchor or even as custom component exposed by some router:
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
<>
|
|
216
|
+
<Button>I'm a button</Button>
|
|
217
|
+
<Button as="a" href="/">
|
|
218
|
+
I'm a link!
|
|
219
|
+
</Button>
|
|
220
|
+
<Button as={Link} to="/">
|
|
221
|
+
I'm a styled Link component
|
|
222
|
+
</Button>
|
|
223
|
+
</>
|
|
229
224
|
```
|
|
230
225
|
|
|
231
226
|
# Tailwind IntelliSense
|
|
@@ -254,10 +249,10 @@ You can then add the following line to your `settings.json`:
|
|
|
254
249
|
"tailwindCSS.experimental.classRegex": ["tw`(.+?)`"]
|
|
255
250
|
```
|
|
256
251
|
|
|
257
|
-
>
|
|
258
|
-
> The `tw` helper function is just an alias for [`String.raw`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw).
|
|
252
|
+
> [!NOTE]
|
|
253
|
+
> The `tw` helper function is just an alias for [`String.raw()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw) which has the nice side effect backslashes are not treated as [escape character in JSX](https://tailwindcss.com/docs/adding-custom-styles#handling-whitespace).
|
|
259
254
|
|
|
260
|
-
In order to get type coverage even for your Tailwind classes you can use a tool like [tailwind-ts](https://github.com/mathieutu/tailwind-ts).
|
|
255
|
+
In order to get type coverage even for your Tailwind classes, you can use a tool like [tailwind-ts](https://github.com/mathieutu/tailwind-ts).
|
|
261
256
|
|
|
262
257
|
# License
|
|
263
258
|
|
package/index.html
CHANGED
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
|
-
<
|
|
14
|
-
<
|
|
13
|
+
<h1>React</h1>
|
|
14
|
+
<div id="react-root"></div>
|
|
15
|
+
<h1>Preact</h1>
|
|
16
|
+
<div id="preact-root"></div>
|
|
17
|
+
<script type="module" src="./src/example/index.ts"></script>
|
|
15
18
|
</body>
|
|
16
19
|
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createElement } from "react";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
import { render, h } from "preact";
|
|
4
|
+
import { ReactApp } from "./react";
|
|
5
|
+
import { PreactApp } from "./preact";
|
|
6
|
+
const root = createRoot(document.getElementById("react-root"));
|
|
7
|
+
root.render(createElement(ReactApp));
|
|
8
|
+
render(h(PreactApp, {}), document.getElementById("preact-root"));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** @jsx h */
|
|
2
|
+
import { h } from "preact";
|
|
3
|
+
export declare const ExpectErrors: <As extends keyof h.JSX.IntrinsicElements | import("preact").ComponentType<any> = "div">(props: {
|
|
4
|
+
as?: As | undefined;
|
|
5
|
+
} & Omit<import("preact").ComponentProps<As>, "as"> & {
|
|
6
|
+
color: "neutral" | "accent";
|
|
7
|
+
} & {}) => import("preact").VNode<{}> | null;
|
|
8
|
+
export declare function WithErrors(): h.JSX.Element;
|
|
9
|
+
export declare function PreactApp(): h.JSX.Element;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/** @jsx h */
|
|
2
|
+
import { styled } from "../preact";
|
|
3
|
+
import { h } from "preact";
|
|
4
|
+
function CustomComponent({ title, ...props }) {
|
|
5
|
+
return h("div", { ...props }, title);
|
|
6
|
+
}
|
|
7
|
+
const Card = styled("div", "bg-white p-4 border-2 rounded-lg");
|
|
8
|
+
const TitleCard = styled(CustomComponent, "bg-white p-4 border-2 rounded-lg");
|
|
9
|
+
const Button = styled("button", {
|
|
10
|
+
base: "px-5 py-2 text-white disabled:bg-gray-400 disabled:text-gray-300",
|
|
11
|
+
variants: {
|
|
12
|
+
color: {
|
|
13
|
+
neutral: "bg-slate-500 hover:bg-slate-400",
|
|
14
|
+
accent: "bg-teal-500 hover:bg-teal-400",
|
|
15
|
+
},
|
|
16
|
+
outlined: {
|
|
17
|
+
true: "border-2",
|
|
18
|
+
},
|
|
19
|
+
rounded: {
|
|
20
|
+
true: "rounded-full",
|
|
21
|
+
false: "rounded-sm",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
compoundVariants: [
|
|
25
|
+
{
|
|
26
|
+
variants: { color: "accent", outlined: true },
|
|
27
|
+
className: "border-teal-600",
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
color: "neutral",
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
export const ExpectErrors = styled("div", {
|
|
35
|
+
variants: {
|
|
36
|
+
color: {
|
|
37
|
+
neutral: "grey",
|
|
38
|
+
accent: "hotpink",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
compoundVariants: [
|
|
42
|
+
{
|
|
43
|
+
//@ts-expect-error
|
|
44
|
+
variants: { outlined: true },
|
|
45
|
+
className: "",
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
defaultVariants: {
|
|
49
|
+
//@ts-expect-error
|
|
50
|
+
outlined: true,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
export function WithErrors() {
|
|
54
|
+
return (h("div", null,
|
|
55
|
+
h(Button, { foo: true }, "unknown property"),
|
|
56
|
+
h(Card, { foo: true }, "Unknown property"),
|
|
57
|
+
h(Button, { color: "foo" }, "Invalid variant")));
|
|
58
|
+
}
|
|
59
|
+
export function PreactApp() {
|
|
60
|
+
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"),
|
|
65
|
+
h(TitleCard, { title: "Hello" }),
|
|
66
|
+
h(Card, null,
|
|
67
|
+
h("h1", null, "Hello"),
|
|
68
|
+
h("p", null, "world")),
|
|
69
|
+
h(Card, { as: "a", href: "https://example.com" }, "Link"),
|
|
70
|
+
h(Card, { as: CustomComponent, title: "Test" })));
|
|
71
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export declare const ExpectErrors: <As extends React.ElementType<any> = "div">(props: {
|
|
3
|
+
as?: As | undefined;
|
|
4
|
+
} & Omit<React.ComponentProps<As>, "as"> & {
|
|
5
|
+
color: "neutral" | "accent";
|
|
6
|
+
} & {}) => React.ReactElement<any, string | React.JSXElementConstructor<any>> | null;
|
|
7
|
+
export declare function WithErrors(): JSX.Element;
|
|
8
|
+
export declare function ReactApp(): JSX.Element;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { styled } from "../react";
|
|
3
|
+
function CustomComponent({ title, ...props }) {
|
|
4
|
+
return React.createElement("div", { ...props }, title);
|
|
5
|
+
}
|
|
6
|
+
const Card = styled("div", "bg-white p-4 border-2 rounded-lg");
|
|
7
|
+
const TitleCard = styled(CustomComponent, "bg-white p-4 border-2 rounded-lg");
|
|
8
|
+
const Button = styled("button", {
|
|
9
|
+
base: "px-5 py-2 text-white disabled:bg-gray-400 disabled:text-gray-300",
|
|
10
|
+
variants: {
|
|
11
|
+
color: {
|
|
12
|
+
neutral: "bg-slate-500 hover:bg-slate-400",
|
|
13
|
+
accent: "bg-teal-500 hover:bg-teal-400",
|
|
14
|
+
},
|
|
15
|
+
outlined: {
|
|
16
|
+
true: "border-2",
|
|
17
|
+
},
|
|
18
|
+
rounded: {
|
|
19
|
+
true: "rounded-full",
|
|
20
|
+
false: "rounded-sm",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
compoundVariants: [
|
|
24
|
+
{
|
|
25
|
+
variants: { color: "accent", outlined: true },
|
|
26
|
+
className: "border-teal-600",
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
defaultVariants: {
|
|
30
|
+
color: "neutral",
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
export const ExpectErrors = styled("div", {
|
|
34
|
+
variants: {
|
|
35
|
+
color: {
|
|
36
|
+
neutral: "grey",
|
|
37
|
+
accent: "hotpink",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
compoundVariants: [
|
|
41
|
+
{
|
|
42
|
+
//@ts-expect-error
|
|
43
|
+
variants: { outlined: true },
|
|
44
|
+
className: "",
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
defaultVariants: {
|
|
48
|
+
//@ts-expect-error
|
|
49
|
+
outlined: true,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
export function WithErrors() {
|
|
53
|
+
return (React.createElement("div", null,
|
|
54
|
+
React.createElement(Button, { foo: true }, "unknown property"),
|
|
55
|
+
React.createElement(Card, { foo: true }, "Unknown property"),
|
|
56
|
+
React.createElement(Button, { color: "foo" }, "Invalid variant"),
|
|
57
|
+
React.createElement(Card, { as: "b", href: "https://example.com" }, "B tags don't have a href attribute")));
|
|
58
|
+
}
|
|
59
|
+
export function ReactApp() {
|
|
60
|
+
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"),
|
|
65
|
+
React.createElement(TitleCard, { title: "Hello" }),
|
|
66
|
+
React.createElement(Card, null,
|
|
67
|
+
React.createElement("h1", null, "Hello"),
|
|
68
|
+
React.createElement("p", null, "world")),
|
|
69
|
+
React.createElement(Card, { as: "a", href: "https://example.com" }, "Link"),
|
|
70
|
+
React.createElement(Card, { as: CustomComponent, title: "Test" })));
|
|
71
|
+
}
|
package/lib/preact.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { JSX, ComponentType, ComponentProps, VNode } from "preact";
|
|
2
|
+
type ElementType<P = any> = keyof JSX.IntrinsicElements | ComponentType<P>;
|
|
3
|
+
import { Variants, VariantsConfig, VariantOptions, Simplify } from "./index.js";
|
|
4
|
+
/**
|
|
5
|
+
* Utility type to infer the first argument of a variantProps function.
|
|
6
|
+
*/
|
|
7
|
+
export type VariantPropsOf<T> = T extends (props: infer P) => any ? P : never;
|
|
8
|
+
/**
|
|
9
|
+
* Type for the variantProps() argument – consists of the VariantOptions and an optional className for chaining.
|
|
10
|
+
*/
|
|
11
|
+
type VariantProps<C extends VariantsConfig<V>, V extends Variants = C["variants"]> = VariantOptions<C> & {
|
|
12
|
+
class?: string;
|
|
13
|
+
className?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function variantProps<C extends VariantsConfig<V>, V extends Variants = C["variants"]>(config: Simplify<C>): <P extends VariantProps<C, C["variants"]>>(props: P) => {
|
|
16
|
+
class: string;
|
|
17
|
+
} & Omit<P, keyof C["variants"]>;
|
|
18
|
+
type VariantsOf<T, V> = T extends VariantsConfig ? V : {};
|
|
19
|
+
type AsProps<T extends ElementType = ElementType> = {
|
|
20
|
+
as?: T;
|
|
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: AsProps<As> & Omit<ComponentProps<As>, "as"> & (C["variants"] extends infer T_1 extends Variants ? { [K in keyof T_1 as K extends keyof (C["variants"] extends infer T_2 extends Variants ? { [K_1 in keyof T_2 as C["variants"][K_1] extends {
|
|
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;
|
|
32
|
+
/**
|
|
33
|
+
* No-op function to mark template literals as tailwind strings.
|
|
34
|
+
*/
|
|
35
|
+
export declare const tw: (template: {
|
|
36
|
+
raw: readonly string[] | ArrayLike<string>;
|
|
37
|
+
}, ...substitutions: any[]) => string;
|
|
38
|
+
export {};
|
package/lib/preact.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { h } from "preact";
|
|
2
|
+
import { forwardRef } from "preact/compat";
|
|
3
|
+
import { variants, } from "./index.js";
|
|
4
|
+
export function variantProps(config) {
|
|
5
|
+
const variantClassName = variants(config);
|
|
6
|
+
return (props) => {
|
|
7
|
+
const result = {};
|
|
8
|
+
// Pass-through all unrelated props
|
|
9
|
+
for (let prop in props) {
|
|
10
|
+
if (config.variants && !(prop in config.variants)) {
|
|
11
|
+
result[prop] = props[prop];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
// Add the optionally passed class/className prop for chaining
|
|
15
|
+
result.class = [variantClassName(props), props.class, props.className]
|
|
16
|
+
.filter(Boolean)
|
|
17
|
+
.join(" ");
|
|
18
|
+
return result;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function styled(type, config) {
|
|
22
|
+
const styledProps = typeof config === "string"
|
|
23
|
+
? variantProps({ base: config, variants: {} })
|
|
24
|
+
: variantProps(config);
|
|
25
|
+
const Component = forwardRef(({ as, ...props }, ref) => {
|
|
26
|
+
return h(as !== null && as !== void 0 ? as : type, { ...styledProps(props), ref });
|
|
27
|
+
});
|
|
28
|
+
return Component;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* No-op function to mark template literals as tailwind strings.
|
|
32
|
+
*/
|
|
33
|
+
export const tw = String.raw;
|
package/lib/react.js
CHANGED
|
@@ -11,7 +11,7 @@ export function variantProps(config) {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
// Add the optionally passed className prop for chaining
|
|
14
|
-
result.className = [props
|
|
14
|
+
result.className = [variantClassName(props), props.className]
|
|
15
15
|
.filter(Boolean)
|
|
16
16
|
.join(" ");
|
|
17
17
|
return result;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "classname-variants",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Variant API for plain class names",
|
|
5
5
|
"author": "Felix Gnass <fgnass@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
"./react": {
|
|
17
17
|
"import": "./lib/react.js",
|
|
18
18
|
"type": "./lib/react.d.ts"
|
|
19
|
+
},
|
|
20
|
+
"./preact": {
|
|
21
|
+
"import": "./lib/preact.js",
|
|
22
|
+
"type": "./lib/preact.d.ts"
|
|
19
23
|
}
|
|
20
24
|
},
|
|
21
25
|
"typesVersions": {
|
|
@@ -25,6 +29,9 @@
|
|
|
25
29
|
],
|
|
26
30
|
"react": [
|
|
27
31
|
"./lib/react.d.ts"
|
|
32
|
+
],
|
|
33
|
+
"preact": [
|
|
34
|
+
"./lib/preact.d.ts"
|
|
28
35
|
]
|
|
29
36
|
}
|
|
30
37
|
},
|
|
@@ -38,11 +45,13 @@
|
|
|
38
45
|
"css",
|
|
39
46
|
"classname",
|
|
40
47
|
"variants",
|
|
41
|
-
"react"
|
|
48
|
+
"react",
|
|
49
|
+
"preact"
|
|
42
50
|
],
|
|
43
51
|
"devDependencies": {
|
|
44
52
|
"@types/react": "^18.0.26",
|
|
45
53
|
"@types/react-dom": "^18.0.9",
|
|
54
|
+
"preact": "^10.20.2",
|
|
46
55
|
"react": "^18.2.0",
|
|
47
56
|
"react-dom": "^18.2.0",
|
|
48
57
|
"typescript": "^4.9.4"
|