@krizaka/ui 1.3.0 → 2.0.0-beta.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/dist/theme.js ADDED
@@ -0,0 +1,140 @@
1
+ "use client";
2
+
3
+ // src/theme/theme.tsx
4
+ import * as React from "react";
5
+
6
+ // src/button/button.tsx
7
+ import { Slot } from "radix-ui";
8
+ import { tv } from "tailwind-variants";
9
+ import { jsx } from "react/jsx-runtime";
10
+ var buttonVariants = tv({
11
+ base: "inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 disabled:pointer-events-none disabled:opacity-40 data-loading:cursor-progress active:scale-[0.98]",
12
+ variants: {
13
+ variant: {
14
+ primary: "bg-accent text-on-accent shadow-sm hover:bg-accent-hover",
15
+ secondary: "border border-border-default bg-surface-2 text-fg hover:border-border-strong hover:bg-surface-3",
16
+ outline: "border border-border-default bg-transparent text-fg hover:border-accent hover:text-accent",
17
+ ghost: "text-fg-secondary hover:bg-surface-2 hover:text-fg",
18
+ // The danger token is invariant and too light for small text on a light surface (3.6:1): the label stays a
19
+ // text role, the border and the tint carry the danger.
20
+ danger: "border border-danger/50 bg-danger/10 text-fg hover:border-danger hover:bg-danger/20"
21
+ },
22
+ size: { sm: "h-8 px-3 text-xs", md: "h-10 px-4 text-sm", lg: "h-12 px-6 text-sm", icon: "h-10 w-10" },
23
+ shape: { rounded: "rounded-lg", pill: "rounded-full" }
24
+ },
25
+ defaultVariants: { variant: "secondary", size: "md", shape: "rounded" }
26
+ });
27
+
28
+ // src/theme/theme.tsx
29
+ import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
30
+ var MODE_KEY = "kz-theme";
31
+ var NAME_KEY = "kz-theme-name";
32
+ var MODES = ["dark", "light", "system"];
33
+ var DARK_QUERY = "(prefers-color-scheme: dark)";
34
+ var isMode = (value) => MODES.includes(value);
35
+ var memory = /* @__PURE__ */ new Map();
36
+ function read(key) {
37
+ try {
38
+ return localStorage.getItem(key);
39
+ } catch {
40
+ return memory.get(key) ?? null;
41
+ }
42
+ }
43
+ function write(key, value) {
44
+ if (value === null) memory.delete(key);
45
+ else memory.set(key, value);
46
+ try {
47
+ if (value === null) localStorage.removeItem(key);
48
+ else localStorage.setItem(key, value);
49
+ } catch {
50
+ }
51
+ }
52
+ var listeners = /* @__PURE__ */ new Set();
53
+ var notify = () => listeners.forEach((listener) => listener());
54
+ function subscribe(listener) {
55
+ listeners.add(listener);
56
+ window.addEventListener("storage", listener);
57
+ return () => {
58
+ listeners.delete(listener);
59
+ window.removeEventListener("storage", listener);
60
+ };
61
+ }
62
+ function applyMode(mode) {
63
+ const dark = window.matchMedia(DARK_QUERY).matches;
64
+ document.documentElement.classList.toggle("light", mode === "light" || mode === "system" && !dark);
65
+ }
66
+ function applyName(previous, next) {
67
+ const classes = document.documentElement.classList;
68
+ if (previous) classes.remove(`theme-${previous}`);
69
+ if (next) classes.add(`theme-${next}`);
70
+ }
71
+ function ThemeScript({ nonce }) {
72
+ const js = `(function(){try{var m=localStorage.getItem("${MODE_KEY}")||"system";var n=localStorage.getItem("${NAME_KEY}");var d=matchMedia("${DARK_QUERY}").matches;var c=document.documentElement.classList;c.toggle("light",m==="light"||(m==="system"&&!d));if(n)c.add("theme-"+n)}catch(e){}})()`;
73
+ return /* @__PURE__ */ jsx2("script", { nonce, dangerouslySetInnerHTML: { __html: js } });
74
+ }
75
+ var ThemeContext = React.createContext(null);
76
+ function ThemeProvider({ children, defaultMode = "system" }) {
77
+ const stored = React.useSyncExternalStore(subscribe, () => read(MODE_KEY), () => null);
78
+ const mode = isMode(stored) ? stored : defaultMode;
79
+ const theme = React.useSyncExternalStore(subscribe, () => read(NAME_KEY), () => null);
80
+ React.useEffect(() => {
81
+ if (mode !== "system") return;
82
+ const query = window.matchMedia(DARK_QUERY);
83
+ const onChange = () => applyMode("system");
84
+ query.addEventListener?.("change", onChange);
85
+ return () => query.removeEventListener?.("change", onChange);
86
+ }, [mode]);
87
+ const setMode = React.useCallback((next) => {
88
+ write(MODE_KEY, next);
89
+ applyMode(next);
90
+ notify();
91
+ }, []);
92
+ const setTheme = React.useCallback((next) => {
93
+ applyName(read(NAME_KEY), next);
94
+ write(NAME_KEY, next);
95
+ notify();
96
+ }, []);
97
+ const value = React.useMemo(() => ({ mode, setMode, theme, setTheme }), [mode, setMode, theme, setTheme]);
98
+ return /* @__PURE__ */ jsx2(ThemeContext.Provider, { value, children });
99
+ }
100
+ function useTheme() {
101
+ const context = React.useContext(ThemeContext);
102
+ if (!context) throw new Error("useTheme: wrap the app in <ThemeProvider>");
103
+ return context;
104
+ }
105
+ var ICONS = {
106
+ dark: /* @__PURE__ */ jsx2("path", { d: "M20 14.5A8 8 0 0 1 9.5 4a8 8 0 1 0 10.5 10.5Z" }),
107
+ light: /* @__PURE__ */ jsxs(Fragment, { children: [
108
+ /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "4" }),
109
+ /* @__PURE__ */ jsx2("path", { d: "M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4" })
110
+ ] }),
111
+ system: /* @__PURE__ */ jsxs(Fragment, { children: [
112
+ /* @__PURE__ */ jsx2("rect", { x: "3", y: "4", width: "18", height: "12", rx: "2" }),
113
+ /* @__PURE__ */ jsx2("path", { d: "M8 20h8M12 16v4" })
114
+ ] })
115
+ };
116
+ function ThemeToggle({ label, variant = "ghost", shape, className, ...props }) {
117
+ const { mode, setMode } = useTheme();
118
+ const next = MODES[(MODES.indexOf(mode) + 1) % MODES.length];
119
+ const name = typeof label === "function" ? label(mode) : label;
120
+ return /* @__PURE__ */ jsx2(
121
+ "button",
122
+ {
123
+ type: "button",
124
+ "aria-label": name,
125
+ title: name,
126
+ "data-mode": mode,
127
+ onClick: () => setMode(next),
128
+ className: buttonVariants({ variant, shape, size: "icon", className }),
129
+ ...props,
130
+ children: /* @__PURE__ */ jsx2("svg", { viewBox: "0 0 24 24", className: "h-5 w-5", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: ICONS[mode] })
131
+ }
132
+ );
133
+ }
134
+ export {
135
+ ThemeProvider,
136
+ ThemeScript,
137
+ ThemeToggle,
138
+ useTheme
139
+ };
140
+ //# sourceMappingURL=theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/theme/theme.tsx","../src/button/button.tsx"],"sourcesContent":["// Client. ONE mechanism for the whole organisation: dark by default, `html.light` for light, `html.theme-<name>` for a\n// named theme, persisted in localStorage (`kz-theme` for the mode, `kz-theme-name` for the named theme).\nimport * as React from \"react\";\n\nimport { type ButtonVariants, buttonVariants } from \"../button/button\";\n\nexport type Mode = \"dark\" | \"light\" | \"system\";\n\nconst MODE_KEY = \"kz-theme\";\nconst NAME_KEY = \"kz-theme-name\";\nconst MODES: readonly Mode[] = [\"dark\", \"light\", \"system\"];\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\";\n\nconst isMode = (value: unknown): value is Mode => MODES.includes(value as Mode);\n\n// Storage blocked (private mode, sandbox): the choice lives in memory for the page.\nconst memory = new Map<string, string>();\n\nfunction read(key: string): string | null {\n try {\n return localStorage.getItem(key);\n } catch {\n return memory.get(key) ?? null;\n }\n}\n\nfunction write(key: string, value: string | null) {\n if (value === null) memory.delete(key);\n else memory.set(key, value);\n try {\n if (value === null) localStorage.removeItem(key);\n else localStorage.setItem(key, value);\n } catch {\n // storage blocked: memory holds it\n }\n}\n\n// The persisted choice is an external store: every provider and tab reads the same value, the server reads none.\nconst listeners = new Set<() => void>();\nconst notify = () => listeners.forEach((listener) => listener());\nfunction subscribe(listener: () => void) {\n listeners.add(listener);\n window.addEventListener(\"storage\", listener);\n return () => {\n listeners.delete(listener);\n window.removeEventListener(\"storage\", listener);\n };\n}\n\nfunction applyMode(mode: Mode) {\n const dark = window.matchMedia(DARK_QUERY).matches;\n document.documentElement.classList.toggle(\"light\", mode === \"light\" || (mode === \"system\" && !dark));\n}\n\nfunction applyName(previous: string | null, next: string | null) {\n const classes = document.documentElement.classList;\n if (previous) classes.remove(`theme-${previous}`);\n if (next) classes.add(`theme-${next}`);\n}\n\n/**\n * In <head>, before anything renders: applies the persisted mode and named theme, so the page never flashes.\n * `nonce` for a strict Content-Security-Policy.\n */\nexport function ThemeScript({ nonce }: { nonce?: string }) {\n const js =\n `(function(){try{var m=localStorage.getItem(\"${MODE_KEY}\")||\"system\";var n=localStorage.getItem(\"${NAME_KEY}\");` +\n `var d=matchMedia(\"${DARK_QUERY}\").matches;var c=document.documentElement.classList;` +\n `c.toggle(\"light\",m===\"light\"||(m===\"system\"&&!d));if(n)c.add(\"theme-\"+n)}catch(e){}})()`;\n return <script nonce={nonce} dangerouslySetInnerHTML={{ __html: js }} />;\n}\n\nexport type ThemeContextValue = {\n /** The chosen mode: dark, light or follow the system. */\n mode: Mode;\n setMode: (mode: Mode) => void;\n /** The named theme (`html.theme-<name>`), or null. */\n theme: string | null;\n setTheme: (theme: string | null) => void;\n};\n\nconst ThemeContext = React.createContext<ThemeContextValue | null>(null);\n\nexport type ThemeProviderProps = {\n children: React.ReactNode;\n /** The mode before anything is persisted. */\n defaultMode?: Mode;\n};\n\nexport function ThemeProvider({ children, defaultMode = \"system\" }: ThemeProviderProps) {\n // Before hydration the server knows nothing persisted (ThemeScript has already applied it to <html>).\n const stored = React.useSyncExternalStore(subscribe, () => read(MODE_KEY), () => null);\n const mode: Mode = isMode(stored) ? stored : defaultMode;\n const theme = React.useSyncExternalStore(subscribe, () => read(NAME_KEY), () => null);\n\n // \"system\" follows the operating system while the page is open.\n React.useEffect(() => {\n if (mode !== \"system\") return;\n const query = window.matchMedia(DARK_QUERY);\n const onChange = () => applyMode(\"system\");\n query.addEventListener?.(\"change\", onChange);\n return () => query.removeEventListener?.(\"change\", onChange);\n }, [mode]);\n\n const setMode = React.useCallback((next: Mode) => {\n write(MODE_KEY, next);\n applyMode(next);\n notify();\n }, []);\n\n const setTheme = React.useCallback((next: string | null) => {\n applyName(read(NAME_KEY), next);\n write(NAME_KEY, next);\n notify();\n }, []);\n\n const value = React.useMemo(() => ({ mode, setMode, theme, setTheme }), [mode, setMode, theme, setTheme]);\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\nexport function useTheme(): ThemeContextValue {\n const context = React.useContext(ThemeContext);\n if (!context) throw new Error(\"useTheme: wrap the app in <ThemeProvider>\");\n return context;\n}\n\nconst ICONS: Record<Mode, React.ReactNode> = {\n dark: <path d=\"M20 14.5A8 8 0 0 1 9.5 4a8 8 0 1 0 10.5 10.5Z\" />,\n light: (\n <>\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n <path d=\"M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4\" />\n </>\n ),\n system: (\n <>\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"12\" rx=\"2\" />\n <path d=\"M8 20h8M12 16v4\" />\n </>\n ),\n};\n\nexport type ThemeToggleProps = Omit<React.ComponentProps<\"button\">, \"onClick\" | \"children\"> &\n Pick<ButtonVariants, \"variant\" | \"shape\"> & {\n /** The accessible name — a string, or one per current mode (e.g. `(mode) => t(\"theme.\" + mode)`). */\n label: string | ((mode: Mode) => string);\n };\n\n/**\n * An icon button (styled as `IconButton`, without its Slot) that cycles dark → light → system. Needs a\n * <ThemeProvider> above it. State: `data-mode`.\n */\nexport function ThemeToggle({ label, variant = \"ghost\", shape, className, ...props }: ThemeToggleProps) {\n const { mode, setMode } = useTheme();\n const next = MODES[(MODES.indexOf(mode) + 1) % MODES.length] as Mode;\n const name = typeof label === \"function\" ? label(mode) : label;\n return (\n <button\n type=\"button\"\n aria-label={name}\n title={name}\n data-mode={mode}\n onClick={() => setMode(next)}\n className={buttonVariants({ variant, shape, size: \"icon\", className })}\n {...props}\n >\n <svg viewBox=\"0 0 24 24\" className=\"h-5 w-5\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n {ICONS[mode]}\n </svg>\n </button>\n );\n}\n","// Server-safe: no hook, no context. `buttonVariants` styles a <Link> or a Server Component without the component.\nimport { Slot } from \"radix-ui\";\nimport type * as React from \"react\";\nimport { tv, type VariantProps } from \"tailwind-variants\";\n\nexport const buttonVariants = tv({\n base:\n \"inline-flex shrink-0 select-none items-center justify-center gap-2 whitespace-nowrap font-semibold transition-all \" +\n \"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-0 \" +\n \"disabled:pointer-events-none disabled:opacity-40 data-loading:cursor-progress active:scale-[0.98]\",\n variants: {\n variant: {\n primary: \"bg-accent text-on-accent shadow-sm hover:bg-accent-hover\",\n secondary: \"border border-border-default bg-surface-2 text-fg hover:border-border-strong hover:bg-surface-3\",\n outline: \"border border-border-default bg-transparent text-fg hover:border-accent hover:text-accent\",\n ghost: \"text-fg-secondary hover:bg-surface-2 hover:text-fg\",\n // The danger token is invariant and too light for small text on a light surface (3.6:1): the label stays a\n // text role, the border and the tint carry the danger.\n danger: \"border border-danger/50 bg-danger/10 text-fg hover:border-danger hover:bg-danger/20\",\n },\n size: { sm: \"h-8 px-3 text-xs\", md: \"h-10 px-4 text-sm\", lg: \"h-12 px-6 text-sm\", icon: \"h-10 w-10\" },\n shape: { rounded: \"rounded-lg\", pill: \"rounded-full\" },\n },\n defaultVariants: { variant: \"secondary\", size: \"md\", shape: \"rounded\" },\n});\n\nexport type ButtonVariants = VariantProps<typeof buttonVariants>;\n\nexport type ButtonProps = React.ComponentProps<\"button\"> &\n ButtonVariants & {\n /** Renders the child (e.g. a <Link>) instead of the <button>, with props, classes and ref merged. */\n asChild?: boolean;\n /** Disables and signals the wait (aria-busy, data-loading) — the product adds a spinner if it wants one. */\n loading?: boolean;\n };\n\nexport function Button({ asChild, loading, variant, size, shape, className, disabled, ...props }: ButtonProps) {\n const Comp = asChild ? Slot.Root : \"button\";\n return (\n <Comp\n type={asChild ? undefined : \"button\"}\n aria-busy={loading || undefined}\n data-loading={loading ? \"\" : undefined}\n data-variant={variant ?? \"secondary\"}\n disabled={asChild ? undefined : disabled || loading}\n aria-disabled={asChild && (disabled || loading) ? true : undefined}\n className={buttonVariants({ variant, size, shape, className })}\n {...props}\n />\n );\n}\n\nexport type IconButtonProps = Omit<ButtonProps, \"size\"> & {\n /** The accessible name (and tooltip) of the button — required, passed translated. */\n label: string;\n size?: ButtonProps[\"size\"];\n};\n\n/** An icon-only button: `label` is required, it is its accessible name. */\nexport function IconButton({ label, size = \"icon\", ...props }: IconButtonProps) {\n return <Button aria-label={label} title={label} size={size} {...props} />;\n}\n"],"mappings":"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","names":["jsx"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@krizaka/ui",
3
- "version": "1.3.0",
4
- "description": "The Krizaka brand layer shared by every product: the animated marks (Krizaka, Orazaka, Orochia) and the Krizaka motion signature, in CSS and React.",
3
+ "version": "2.0.0-beta.0",
4
+ "description": "The Krizaka front-end platform's components: the primitives (button, badge, avatar, field, theme, countdown…) on the semantic --kz-* tokens, the animated marks (Krizaka, Orazaka, Orochia) and the motion signature — web and /native.",
5
5
  "keywords": [
6
6
  "krizaka",
7
7
  "orazaka",
@@ -11,7 +11,10 @@
11
11
  "motion",
12
12
  "animation",
13
13
  "design-system",
14
- "brand"
14
+ "brand",
15
+ "primitives",
16
+ "tailwindcss",
17
+ "components"
15
18
  ],
16
19
  "homepage": "https://github.com/krizaka/krizaka-ui#readme",
17
20
  "bugs": "https://github.com/krizaka/krizaka-ui/issues",
@@ -31,13 +34,57 @@
31
34
  "types": "./dist/index.d.ts",
32
35
  "import": "./dist/index.js"
33
36
  },
37
+ "./cn": {
38
+ "types": "./dist/cn.d.ts",
39
+ "import": "./dist/cn.js"
40
+ },
41
+ "./slot": {
42
+ "types": "./dist/slot.d.ts",
43
+ "import": "./dist/slot.js"
44
+ },
45
+ "./button": {
46
+ "types": "./dist/button.d.ts",
47
+ "import": "./dist/button.js"
48
+ },
49
+ "./badge": {
50
+ "types": "./dist/badge.d.ts",
51
+ "import": "./dist/badge.js"
52
+ },
53
+ "./field": {
54
+ "types": "./dist/field.d.ts",
55
+ "import": "./dist/field.js"
56
+ },
57
+ "./skeleton": {
58
+ "types": "./dist/skeleton.d.ts",
59
+ "import": "./dist/skeleton.js"
60
+ },
61
+ "./empty-state": {
62
+ "types": "./dist/empty-state.d.ts",
63
+ "import": "./dist/empty-state.js"
64
+ },
65
+ "./spinner": {
66
+ "types": "./dist/spinner.d.ts",
67
+ "import": "./dist/spinner.js"
68
+ },
69
+ "./avatar": {
70
+ "types": "./dist/avatar.d.ts",
71
+ "import": "./dist/avatar.js"
72
+ },
73
+ "./theme": {
74
+ "types": "./dist/theme.d.ts",
75
+ "import": "./dist/theme.js"
76
+ },
77
+ "./countdown": {
78
+ "types": "./dist/countdown.d.ts",
79
+ "import": "./dist/countdown.js"
80
+ },
34
81
  "./motion.css": "./dist/motion.css",
35
82
  "./tailwind.css": "./tailwind.css",
36
- "./package.json": "./package.json",
37
83
  "./native": {
38
84
  "types": "./dist/native.d.ts",
39
85
  "import": "./dist/native.js"
40
- }
86
+ },
87
+ "./package.json": "./package.json"
41
88
  },
42
89
  "files": [
43
90
  "dist",
@@ -49,10 +96,12 @@
49
96
  "*.css"
50
97
  ],
51
98
  "peerDependencies": {
99
+ "@krizaka/tailwind": ">=2.0.0-beta.0",
52
100
  "react": ">=18",
53
101
  "react-dom": ">=18",
54
102
  "react-native": ">=0.76",
55
- "react-native-svg": ">=15"
103
+ "react-native-svg": ">=15",
104
+ "tailwindcss": "^4.1"
56
105
  },
57
106
  "devDependencies": {
58
107
  "@arethetypeswrong/cli": "^0.18.5",
@@ -60,9 +109,11 @@
60
109
  "@storybook/react-vite": "10.6.1",
61
110
  "@testing-library/dom": "^10.4.0",
62
111
  "@testing-library/react": "^16.3.0",
112
+ "@testing-library/user-event": "^14.6.7",
63
113
  "@types/node": "^22.15.0",
64
114
  "@types/react": "^19.2.0",
65
115
  "@types/react-dom": "^19.2.0",
116
+ "axe-core": "^4.14.0",
66
117
  "eslint": "^9.39.0",
67
118
  "happy-dom": "^20.0.0",
68
119
  "publint": "^0.3.25",
@@ -72,6 +123,7 @@
72
123
  "react-native-svg": "^15.15.4",
73
124
  "size-limit": "^14.2.0",
74
125
  "storybook": "10.6.1",
126
+ "tailwindcss": "^4.3.3",
75
127
  "tsup": "^8.5.1",
76
128
  "typescript": "^5.9.0",
77
129
  "vitest": "^4.1.11",
@@ -84,6 +136,9 @@
84
136
  "access": "public"
85
137
  },
86
138
  "peerDependenciesMeta": {
139
+ "@krizaka/tailwind": {
140
+ "optional": true
141
+ },
87
142
  "react-dom": {
88
143
  "optional": true
89
144
  },
@@ -92,6 +147,9 @@
92
147
  },
93
148
  "react-native-svg": {
94
149
  "optional": true
150
+ },
151
+ "tailwindcss": {
152
+ "optional": true
95
153
  }
96
154
  },
97
155
  "size-limit": [
@@ -106,10 +164,121 @@
106
164
  "path": "dist/native.js",
107
165
  "limit": "6 kB",
108
166
  "gzip": true
167
+ },
168
+ {
169
+ "name": "./cn (the shared runtime: clsx + tailwind-variants and its merge engine)",
170
+ "path": "dist/cn.js",
171
+ "limit": "14 kB",
172
+ "gzip": true
173
+ },
174
+ {
175
+ "name": "./button",
176
+ "path": "dist/button.js",
177
+ "limit": "3 kB",
178
+ "gzip": true,
179
+ "ignore": [
180
+ "tailwind-variants",
181
+ "clsx"
182
+ ]
183
+ },
184
+ {
185
+ "name": "./badge",
186
+ "path": "dist/badge.js",
187
+ "limit": "1 kB",
188
+ "gzip": true,
189
+ "ignore": [
190
+ "tailwind-variants",
191
+ "clsx"
192
+ ]
193
+ },
194
+ {
195
+ "name": "./avatar",
196
+ "path": "dist/avatar.js",
197
+ "limit": "6 kB",
198
+ "gzip": true,
199
+ "ignore": [
200
+ "tailwind-variants",
201
+ "clsx"
202
+ ]
203
+ },
204
+ {
205
+ "name": "./field",
206
+ "path": "dist/field.js",
207
+ "limit": "1.5 kB",
208
+ "gzip": true,
209
+ "ignore": [
210
+ "tailwind-variants",
211
+ "clsx"
212
+ ]
213
+ },
214
+ {
215
+ "name": "./theme",
216
+ "path": "dist/theme.js",
217
+ "limit": "2 kB",
218
+ "gzip": true,
219
+ "ignore": [
220
+ "tailwind-variants",
221
+ "clsx"
222
+ ]
223
+ },
224
+ {
225
+ "name": "./countdown",
226
+ "path": "dist/countdown.js",
227
+ "limit": "2 kB",
228
+ "gzip": true,
229
+ "ignore": [
230
+ "tailwind-variants",
231
+ "clsx"
232
+ ]
233
+ },
234
+ {
235
+ "name": "./skeleton",
236
+ "path": "dist/skeleton.js",
237
+ "limit": "1 kB",
238
+ "gzip": true,
239
+ "ignore": [
240
+ "tailwind-variants",
241
+ "clsx"
242
+ ]
243
+ },
244
+ {
245
+ "name": "./spinner",
246
+ "path": "dist/spinner.js",
247
+ "limit": "1 kB",
248
+ "gzip": true,
249
+ "ignore": [
250
+ "tailwind-variants",
251
+ "clsx"
252
+ ]
253
+ },
254
+ {
255
+ "name": "./empty-state",
256
+ "path": "dist/empty-state.js",
257
+ "limit": "1 kB",
258
+ "gzip": true,
259
+ "ignore": [
260
+ "tailwind-variants",
261
+ "clsx"
262
+ ]
263
+ },
264
+ {
265
+ "name": "./slot",
266
+ "path": "dist/slot.js",
267
+ "limit": "2 kB",
268
+ "gzip": true,
269
+ "ignore": [
270
+ "tailwind-variants",
271
+ "clsx"
272
+ ]
109
273
  }
110
274
  ],
275
+ "dependencies": {
276
+ "clsx": "^2.1.1",
277
+ "radix-ui": "^1.7.0",
278
+ "tailwind-variants": "^3.3.1"
279
+ },
111
280
  "scripts": {
112
- "build": "tsup && node -e \"require('node:fs').copyFileSync('src/motion.css','dist/motion.css')\"",
281
+ "build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsup && node -e \"require('node:fs').copyFileSync('src/motion.css','dist/motion.css')\"",
113
282
  "lint": "eslint . --max-warnings 0 && tsc --noEmit --noUnusedParameters --noFallthroughCasesInSwitch --noImplicitReturns",
114
283
  "typecheck": "tsc --noEmit",
115
284
  "test": "vitest run",