classname-variants 1.3.3 → 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 CHANGED
@@ -1,20 +1,58 @@
1
- # classname-variants
1
+ # classname-variants 🌈
2
2
 
3
- Stitches-like [variant API](https://stitches.dev/docs/variants) for plain class names.
3
+ Library to create type-safe components that render their class name based on a set of variants.
4
4
 
5
- The library is framework-agnostic and can be used with any kind of CSS flavor.
5
+ # Examples
6
6
 
7
- It is especially useful though if used with [Tailwind](https://tailwindcss.com/) or [CSS Modules](https://github.com/css-modules/css-modules) in combination with React, as it provides some [dedicated helpers](#React) and even allows for a _styled-components_ like API, but with [class names instead of styles](#bonus-styled-components-but-with-class-names-)!
7
+ Here is an example that uses React and Tailwind CSS:
8
8
 
9
- [![Edit classname-variants/react](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/classname-variants-react-3bzjl?fontsize=14&hidenavigation=1&theme=dark)
9
+ ```tsx
10
+ import { styled } from "classname-variants/react";
10
11
 
11
- # Basics
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
+ [![Edit classname-variants/react](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/classname-variants-react-3bzjl?fontsize=14&hidenavigation=1&theme=dark)
12
30
 
13
- Let's assume we want to build a button component with Tailwind CSS that comes in different sizes and colors.
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
- It consists of some _base classes_ that are always present as well as some optional classes that need to be added depending on the desired _variants_.
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
- # Advanced Usage
73
+ # API
47
74
 
48
- ## Boolean variants
75
+ ### Defining variants
49
76
 
50
- Variants can be of type `boolean` by using `"true"` as the key:
77
+ You can add any number of variants by using the `variants` key.
51
78
 
52
- ```tsx
53
- const button = variants({
54
- base: "text-white",
79
+ ```ts
80
+ {
55
81
  variants: {
56
- rounded: {
57
- true: "rounded-full",
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
- ## Compound variants
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
- ```tsx
68
- const button = variants({
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
- ## Default variants
139
+ ### Default variants
91
140
 
92
- The `defaultVariants` option can be used to select a variant by default:
141
+ You can use the `defaultVariants` property to set defaults:
93
142
 
94
143
  ```ts
95
- const button = variants({
144
+ {
96
145
  variants: {
97
146
  color: {
98
- neutral: "bg-gray-200",
99
- accent: "bg-teal-400",
147
+ primary: "bg-teal-300",
148
+ secondary: "bg-teal-100"
100
149
  },
101
150
  },
102
151
  defaultVariants: {
103
- color: "neutral",
104
- },
105
- });
152
+ color: "secondary",
153
+ }
154
+ }
106
155
  ```
107
156
 
108
- # React
157
+ ### Base class
109
158
 
110
- The library contains utility functions that are useful for writing React components.
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
- import { variantProps } from "classname-variants/react";
116
-
117
- const buttonProps = variantProps({
118
- base: "rounded-md text-white",
162
+ {
163
+ base: "text-black rounded-full px-2",
119
164
  variants: {
120
- color: {
121
- brand: "bg-sky-500",
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
- # Bonus: styled-components, but for static CSS 💅
170
+ ### Components without variants
158
171
 
159
- Things can be taken even a step further, resulting in a _styled-components_ like way of defining reusable components. Under the hood, this does basically the same as the example above, but also handles _refs_ correctly:
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
- ```ts
162
- import { styled, tw } from "classname-variants/react";
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
- Again, this is not limited to tailwind, so you could do the same with CSS modules:
181
+ ### Styling custom components
175
182
 
176
- ```ts
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
- const Button = styled("button", {
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
- size: {
183
- small: styles.small,
184
- large: styles.large,
185
- },
193
+ // ...
186
194
  },
187
195
  });
188
196
  ```
189
197
 
190
- > [!TIP]
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 "as" prop to do so:
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
- function App() {
220
- return (
221
- <div>
222
- <Button>I'm a button</Button>
223
- <Button as="a" href="/">
224
- I'm a link!
225
- </Button>
226
- </div>
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
package/index.html CHANGED
@@ -10,7 +10,10 @@
10
10
  <script src="https://cdn.tailwindcss.com"></script>
11
11
  </head>
12
12
  <body>
13
- <div id="root"></div>
14
- <script type="module" src="./src/example.tsx"></script>
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
+ }
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "classname-variants",
3
- "version": "1.3.3",
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"