@smicolon/fasla-ui 0.2.2

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.
Files changed (74) hide show
  1. package/README.md +141 -0
  2. package/bin/fasla-ui.mjs +54 -0
  3. package/dist/index.d.ts +5 -0
  4. package/dist/index.js +241 -0
  5. package/dist/lib/utils.d.ts +9 -0
  6. package/dist/lib/utils.js +9 -0
  7. package/dist/motion/index.d.ts +33 -0
  8. package/dist/motion/index.js +59 -0
  9. package/dist/registry/index.d.ts +17 -0
  10. package/dist/registry/index.js +145 -0
  11. package/dist/tokens/index.d.ts +55 -0
  12. package/dist/tokens/index.js +34 -0
  13. package/package.json +97 -0
  14. package/registry/blocks/app-shell/AppShell.tsx +66 -0
  15. package/registry/blocks/app-shell/index.ts +1 -0
  16. package/registry/blocks/data-table/DataTable.tsx +292 -0
  17. package/registry/blocks/data-table/index.ts +7 -0
  18. package/registry/blocks/empty-state/EmptyState.test.tsx +120 -0
  19. package/registry/blocks/empty-state/EmptyState.tsx +164 -0
  20. package/registry/blocks/empty-state/index.ts +6 -0
  21. package/registry/blocks/form-section/FormSection.tsx +140 -0
  22. package/registry/blocks/form-section/index.ts +8 -0
  23. package/registry/blocks/navbar/Navbar.tsx +190 -0
  24. package/registry/blocks/navbar/index.ts +16 -0
  25. package/registry/blocks/page-header/PageHeader.tsx +64 -0
  26. package/registry/blocks/page-header/index.ts +1 -0
  27. package/registry/blocks/sidebar/Sidebar.tsx +202 -0
  28. package/registry/blocks/sidebar/index.ts +16 -0
  29. package/registry/blocks/stats-card/StatsCard.tsx +134 -0
  30. package/registry/blocks/stats-card/index.ts +6 -0
  31. package/registry/effects/animated-gradient/animated-gradient.tsx +136 -0
  32. package/registry/effects/animated-gradient/index.ts +6 -0
  33. package/registry/effects/border-beam/border-beam.tsx +132 -0
  34. package/registry/effects/border-beam/index.ts +6 -0
  35. package/registry/effects/glow-card/glow-card.tsx +145 -0
  36. package/registry/effects/glow-card/index.ts +6 -0
  37. package/registry/effects/shimmer-button/index.ts +1 -0
  38. package/registry/effects/shimmer-button/shimmer-button.tsx +91 -0
  39. package/registry/effects/spotlight/index.ts +6 -0
  40. package/registry/effects/spotlight/spotlight.tsx +162 -0
  41. package/registry/effects/text-reveal/index.ts +6 -0
  42. package/registry/effects/text-reveal/text-reveal.tsx +139 -0
  43. package/registry/effects/typewriter-text/index.ts +6 -0
  44. package/registry/effects/typewriter-text/typewriter-text.tsx +220 -0
  45. package/registry/ui/avatar/avatar.tsx +135 -0
  46. package/registry/ui/avatar/index.ts +1 -0
  47. package/registry/ui/badge/badge.test.tsx +51 -0
  48. package/registry/ui/badge/badge.tsx +39 -0
  49. package/registry/ui/badge/index.ts +1 -0
  50. package/registry/ui/button/button.test.tsx +87 -0
  51. package/registry/ui/button/button.tsx +88 -0
  52. package/registry/ui/button/index.ts +1 -0
  53. package/registry/ui/card/card.test.tsx +119 -0
  54. package/registry/ui/card/card.tsx +75 -0
  55. package/registry/ui/card/index.ts +8 -0
  56. package/registry/ui/checkbox/checkbox.tsx +113 -0
  57. package/registry/ui/checkbox/index.ts +1 -0
  58. package/registry/ui/combobox/combobox.tsx +410 -0
  59. package/registry/ui/combobox/index.ts +1 -0
  60. package/registry/ui/input/index.ts +1 -0
  61. package/registry/ui/input/input.test.tsx +78 -0
  62. package/registry/ui/input/input.tsx +76 -0
  63. package/registry/ui/select/index.ts +1 -0
  64. package/registry/ui/select/select.tsx +94 -0
  65. package/registry/ui/skeleton/index.ts +7 -0
  66. package/registry/ui/skeleton/skeleton.test.tsx +104 -0
  67. package/registry/ui/skeleton/skeleton.tsx +88 -0
  68. package/registry/ui/switch/index.ts +1 -0
  69. package/registry/ui/switch/switch.tsx +89 -0
  70. package/registry/ui/tabs/index.ts +10 -0
  71. package/registry/ui/tabs/tabs.tsx +150 -0
  72. package/registry/ui/textarea/index.ts +1 -0
  73. package/registry/ui/textarea/textarea.tsx +73 -0
  74. package/registry.json +247 -0
package/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # @smicolon/fasla-ui
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@smicolon/fasla-ui.svg)](https://www.npmjs.com/package/@smicolon/fasla-ui)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@smicolon/fasla-ui.svg)](https://www.npmjs.com/package/@smicolon/fasla-ui)
5
+ [![license](https://img.shields.io/npm/l/@smicolon/fasla-ui.svg)](https://github.com/smicolon/fasla-ui/blob/main/LICENSE)
6
+
7
+ A professional React UI component library built on **shadcn/ui** primitives. Provides animated effects components (like Magic UI), app building blocks, and design tokens - all distributed via a shadcn-compatible registry.
8
+
9
+ ## Features
10
+
11
+ - **30+ Components** - UI primitives, app blocks, and animated effects
12
+ - **shadcn Compatible** - Works seamlessly with existing shadcn/ui projects
13
+ - **Tailwind CSS** - Utility-first styling with full customization
14
+ - **Framer Motion** - Smooth animations with `prefers-reduced-motion` support
15
+ - **TypeScript** - Full type safety and IntelliSense
16
+ - **Design Tokens** - Consistent spacing, colors, and density profiles
17
+ - **Accessible** - WCAG compliant with keyboard navigation support
18
+ - **Tree Shakeable** - Import only what you need
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ # Using the CLI (recommended)
24
+ npx @smicolon/cli init
25
+ npx @smicolon/cli add button
26
+
27
+ # Or install the package directly
28
+ npm install @smicolon/fasla-ui
29
+ # or
30
+ yarn add @smicolon/fasla-ui
31
+ # or
32
+ pnpm add @smicolon/fasla-ui
33
+ ```
34
+
35
+ ## Components
36
+
37
+ ### UI Primitives
38
+ Core building blocks with CVA variants for consistent styling:
39
+
40
+ | Component | Description |
41
+ |-----------|-------------|
42
+ | **Button** | Multiple variants (default, destructive, outline, secondary, ghost, link) with loading state |
43
+ | **Input** | Text input with validation states and icon support |
44
+ | **Textarea** | Multi-line text input with auto-resize |
45
+ | **Select** | Dropdown selection with search |
46
+ | **Checkbox** | Checkbox with label and description |
47
+ | **Switch** | Toggle switch with multiple sizes |
48
+ | **Tabs** | Tabbed navigation with animations |
49
+ | **Badge** | Status indicators and labels |
50
+ | **Avatar** | User avatars with fallback initials |
51
+ | **Card** | Flexible content containers |
52
+ | **Skeleton** | Loading placeholders |
53
+ | **Combobox** | Searchable select with autocomplete |
54
+
55
+ ### App Blocks
56
+ Pre-built page compositions for rapid development:
57
+
58
+ | Component | Description |
59
+ |-----------|-------------|
60
+ | **AppShell** | Application layout with sidebar and header |
61
+ | **PageHeader** | Title, breadcrumbs, and action buttons |
62
+ | **DataTable** | Sortable, filterable tables with pagination |
63
+ | **FormSection** | Grouped form fields with validation |
64
+ | **EmptyState** | Empty content placeholders with CTAs |
65
+ | **Sidebar** | Collapsible navigation sidebar |
66
+ | **Navbar** | Top navigation bar |
67
+ | **StatsCard** | Metric display cards |
68
+
69
+ ### Effects
70
+ Animated components for visual flair (powered by Framer Motion):
71
+
72
+ | Component | Description |
73
+ |-----------|-------------|
74
+ | **ShimmerButton** | Button with animated shimmer effect |
75
+ | **AnimatedGradient** | Smooth gradient background animations |
76
+ | **BorderBeam** | Animated border glow effect |
77
+ | **Spotlight** | Cursor-following spotlight |
78
+ | **TextReveal** | Text reveal on scroll |
79
+ | **GlowCard** | Card with hover glow effect |
80
+ | **TypewriterText** | Typewriter text animation |
81
+
82
+ ## Quick Start
83
+
84
+ ```tsx
85
+ import { Button } from "@smicolon/fasla-ui/registry/ui/button"
86
+ import { Card, CardHeader, CardTitle, CardContent } from "@smicolon/fasla-ui/registry/ui/card"
87
+ import { ShimmerButton } from "@smicolon/fasla-ui/registry/effects/shimmer-button"
88
+
89
+ export function MyComponent() {
90
+ return (
91
+ <Card>
92
+ <CardHeader>
93
+ <CardTitle>Welcome</CardTitle>
94
+ </CardHeader>
95
+ <CardContent className="space-x-4">
96
+ <Button variant="default">Get Started</Button>
97
+ <ShimmerButton>Learn More</ShimmerButton>
98
+ </CardContent>
99
+ </Card>
100
+ )
101
+ }
102
+ ```
103
+
104
+ ## Peer Dependencies
105
+
106
+ ```json
107
+ {
108
+ "react": "^18.0.0",
109
+ "react-dom": "^18.0.0",
110
+ "tailwindcss": "^3.4.0",
111
+ "framer-motion": "^11.0.0" // optional, required for effects
112
+ }
113
+ ```
114
+
115
+ ## Tailwind Configuration
116
+
117
+ Add fasla-ui to your `tailwind.config.js`:
118
+
119
+ ```js
120
+ module.exports = {
121
+ content: [
122
+ // ... your content paths
123
+ "./node_modules/@smicolon/fasla-ui/**/*.{js,ts,jsx,tsx}",
124
+ ],
125
+ }
126
+ ```
127
+
128
+ ## Documentation
129
+
130
+ Visit [ui.smicolon.com](https://ui.smicolon.com) for full documentation, examples, and API reference.
131
+
132
+ ## Related
133
+
134
+ - [@smicolon/cli](https://www.npmjs.com/package/@smicolon/cli) - CLI for adding components
135
+ - [shadcn/ui](https://ui.shadcn.com) - The foundation this library builds on
136
+ - [Tailwind CSS](https://tailwindcss.com) - Utility-first CSS framework
137
+ - [Framer Motion](https://www.framer.com/motion/) - Animation library for React
138
+
139
+ ## License
140
+
141
+ MIT - See [LICENSE](https://github.com/smicolon/fasla-ui/blob/main/LICENSE) for details.
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+
5
+ function usage() {
6
+ console.log(`fasla-ui (scaffold)
7
+ Usage:
8
+ fasla-ui add <registry-path> --to <target-dir>
9
+
10
+ Example:
11
+ fasla-ui add blocks/app-shell --to ../client/src/components/blocks
12
+ `);
13
+ }
14
+
15
+ function copyDir(src, dest) {
16
+ fs.mkdirSync(dest, { recursive: true });
17
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
18
+ const srcPath = path.join(src, entry.name);
19
+ const destPath = path.join(dest, entry.name);
20
+ if (entry.isDirectory()) copyDir(srcPath, destPath);
21
+ else fs.copyFileSync(srcPath, destPath);
22
+ }
23
+ }
24
+
25
+ const args = process.argv.slice(2);
26
+ if (args.length === 0 || args.includes("-h") || args.includes("--help")) {
27
+ usage();
28
+ process.exit(0);
29
+ }
30
+
31
+ const cmd = args[0];
32
+ if (cmd !== "add") {
33
+ usage();
34
+ process.exit(1);
35
+ }
36
+
37
+ const item = args[1];
38
+ const toIdx = args.findIndex((a) => a === "--to");
39
+ const targetDir = toIdx >= 0 ? args[toIdx + 1] : null;
40
+ if (!item || !targetDir) {
41
+ usage();
42
+ process.exit(1);
43
+ }
44
+
45
+ const registryRoot = path.resolve(process.cwd(), "packages/fasla-ui/registry");
46
+ const srcDir = path.join(registryRoot, item);
47
+ if (!fs.existsSync(srcDir)) {
48
+ console.error(`Registry path not found: ${srcDir}`);
49
+ process.exit(1);
50
+ }
51
+
52
+ const destDir = path.resolve(process.cwd(), targetDir, path.basename(item));
53
+ copyDir(srcDir, destDir);
54
+ console.log(`Added ${item} -> ${destDir}`);
@@ -0,0 +1,5 @@
1
+ export { cn } from './lib/utils.js';
2
+ export { Density, TokenSet, baseTokens, tailwindSemanticColors } from './tokens/index.js';
3
+ export { MotionPreset, useMotionPresets } from './motion/index.js';
4
+ export { RegistryItem, RegistryItemType, getRegistryItem, getRegistryItemsByCategory, getRegistryItemsByType, registry } from './registry/index.js';
5
+ import 'clsx';
package/dist/index.js ADDED
@@ -0,0 +1,241 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ import { useReducedMotion } from 'framer-motion';
4
+
5
+ // src/lib/utils.ts
6
+ function cn(...inputs) {
7
+ return twMerge(clsx(inputs));
8
+ }
9
+
10
+ // src/tokens/model.ts
11
+ var baseTokens = {
12
+ radius: { sm: "0.5rem", md: "0.75rem", lg: "1rem" },
13
+ spacing: { densityMultiplier: { compact: 0.9, comfortable: 1, spacious: 1.1 } },
14
+ typography: { fontFamily: "Inter", baseFontSizePx: 16, lineHeight: 1.5 },
15
+ motion: { durationsMs: { fast: 120, base: 180, slow: 260 } }
16
+ };
17
+
18
+ // src/tokens/tailwind.ts
19
+ var tailwindSemanticColors = {
20
+ border: "hsl(var(--border))",
21
+ input: "hsl(var(--input))",
22
+ ring: "hsl(var(--ring))",
23
+ background: "hsl(var(--background))",
24
+ foreground: "hsl(var(--foreground))",
25
+ primary: {
26
+ DEFAULT: "hsl(var(--primary))",
27
+ foreground: "hsl(var(--primary-foreground))"
28
+ },
29
+ secondary: {
30
+ DEFAULT: "hsl(var(--secondary))",
31
+ foreground: "hsl(var(--secondary-foreground))"
32
+ },
33
+ muted: {
34
+ DEFAULT: "hsl(var(--muted))",
35
+ foreground: "hsl(var(--muted-foreground))"
36
+ },
37
+ destructive: {
38
+ DEFAULT: "hsl(var(--destructive))",
39
+ foreground: "hsl(var(--destructive-foreground))"
40
+ }
41
+ };
42
+ function useMotionPresets() {
43
+ const prefersReduced = useReducedMotion();
44
+ const durations = {
45
+ fast: 0.12,
46
+ base: 0.18,
47
+ slow: 0.26
48
+ };
49
+ const easing = {
50
+ standard: [0.2, 0, 0, 1],
51
+ emphasized: [0.2, 0.8, 0.2, 1]
52
+ };
53
+ const safe = (preset) => {
54
+ if (!prefersReduced) return preset;
55
+ return {
56
+ initial: { opacity: 0 },
57
+ animate: { opacity: 1 },
58
+ exit: { opacity: 0 },
59
+ transition: { duration: durations.fast }
60
+ };
61
+ };
62
+ return {
63
+ fadeIn: safe({
64
+ initial: { opacity: 0 },
65
+ animate: { opacity: 1 },
66
+ exit: { opacity: 0 },
67
+ transition: { duration: durations.base, ease: easing.standard }
68
+ }),
69
+ slideUp: safe({
70
+ initial: { opacity: 0, y: 10 },
71
+ animate: { opacity: 1, y: 0 },
72
+ exit: { opacity: 0, y: 6 },
73
+ transition: { duration: durations.base, ease: easing.standard }
74
+ }),
75
+ modalScale: safe({
76
+ initial: { opacity: 0, scale: 0.98 },
77
+ animate: { opacity: 1, scale: 1 },
78
+ exit: { opacity: 0, scale: 0.98 },
79
+ transition: { duration: durations.base, ease: easing.emphasized }
80
+ }),
81
+ stagger: {
82
+ container: safe({
83
+ initial: { opacity: 0 },
84
+ animate: { opacity: 1 },
85
+ transition: { staggerChildren: 0.06, delayChildren: 0.02 }
86
+ }),
87
+ item: safe({
88
+ initial: { opacity: 0, y: 8 },
89
+ animate: { opacity: 1, y: 0 },
90
+ transition: { duration: durations.base, ease: easing.standard }
91
+ })
92
+ },
93
+ meta: { prefersReduced, durations, easing }
94
+ };
95
+ }
96
+
97
+ // src/registry/index.ts
98
+ var registry = [
99
+ // UI Primitives
100
+ {
101
+ name: "button",
102
+ type: "ui",
103
+ title: "Button",
104
+ description: "A button component with multiple variants, sizes, and loading state.",
105
+ sourcePath: "registry/ui/button",
106
+ dependencies: ["@radix-ui/react-slot", "class-variance-authority"],
107
+ categories: ["primitives", "forms"]
108
+ },
109
+ {
110
+ name: "input",
111
+ type: "ui",
112
+ title: "Input",
113
+ description: "An input component with variants for validation states and icon support.",
114
+ sourcePath: "registry/ui/input",
115
+ dependencies: ["class-variance-authority"],
116
+ categories: ["primitives", "forms"]
117
+ },
118
+ {
119
+ name: "card",
120
+ type: "ui",
121
+ title: "Card",
122
+ description: "A card component with header, content, and footer sections.",
123
+ sourcePath: "registry/ui/card",
124
+ categories: ["primitives", "layout"]
125
+ },
126
+ {
127
+ name: "badge",
128
+ type: "ui",
129
+ title: "Badge",
130
+ description: "A badge component for status indicators with multiple variants.",
131
+ sourcePath: "registry/ui/badge",
132
+ dependencies: ["class-variance-authority"],
133
+ categories: ["primitives", "data-display"]
134
+ },
135
+ {
136
+ name: "skeleton",
137
+ type: "ui",
138
+ title: "Skeleton",
139
+ description: "Loading skeleton placeholders with pre-built variants for text, avatars, and cards.",
140
+ sourcePath: "registry/ui/skeleton",
141
+ categories: ["primitives", "feedback"]
142
+ },
143
+ // Blocks
144
+ {
145
+ name: "app-shell",
146
+ type: "block",
147
+ title: "App Shell",
148
+ description: "Application shell with sidebar, header, and content area.",
149
+ sourcePath: "registry/blocks/app-shell",
150
+ categories: ["layout", "application"]
151
+ },
152
+ {
153
+ name: "page-header",
154
+ type: "block",
155
+ title: "Page Header",
156
+ description: "Page header with title, breadcrumb, description, and actions.",
157
+ sourcePath: "registry/blocks/page-header",
158
+ categories: ["layout", "navigation"]
159
+ },
160
+ {
161
+ name: "empty-state",
162
+ type: "block",
163
+ title: "Empty State",
164
+ description: "Empty state component with icon, title, description, and actions.",
165
+ sourcePath: "registry/blocks/empty-state",
166
+ categories: ["feedback", "data-display"]
167
+ },
168
+ {
169
+ name: "form-section",
170
+ type: "block",
171
+ title: "Form Section",
172
+ description: "Form section with title, description, fields, and actions.",
173
+ sourcePath: "registry/blocks/form-section",
174
+ categories: ["forms", "layout"]
175
+ },
176
+ {
177
+ name: "data-table",
178
+ type: "block",
179
+ title: "Data Table",
180
+ description: "Data table with sorting, pagination, and custom cell rendering.",
181
+ sourcePath: "registry/blocks/data-table",
182
+ categories: ["data-display", "tables"]
183
+ },
184
+ // Effects
185
+ {
186
+ name: "shimmer-button",
187
+ type: "effect",
188
+ title: "Shimmer Button",
189
+ description: "A button with animated shimmer effect.",
190
+ sourcePath: "registry/effects/shimmer-button",
191
+ dependencies: ["framer-motion"],
192
+ categories: ["effects", "buttons"]
193
+ },
194
+ {
195
+ name: "animated-gradient",
196
+ type: "effect",
197
+ title: "Animated Gradient",
198
+ description: "Animated gradient background and text effects.",
199
+ sourcePath: "registry/effects/animated-gradient",
200
+ dependencies: ["framer-motion"],
201
+ categories: ["effects", "backgrounds"]
202
+ },
203
+ {
204
+ name: "text-reveal",
205
+ type: "effect",
206
+ title: "Text Reveal",
207
+ description: "Character and word reveal animations.",
208
+ sourcePath: "registry/effects/text-reveal",
209
+ dependencies: ["framer-motion"],
210
+ categories: ["effects", "text"]
211
+ },
212
+ {
213
+ name: "border-beam",
214
+ type: "effect",
215
+ title: "Border Beam",
216
+ description: "Animated border beam and glowing border effects.",
217
+ sourcePath: "registry/effects/border-beam",
218
+ dependencies: ["framer-motion"],
219
+ categories: ["effects", "borders"]
220
+ },
221
+ {
222
+ name: "spotlight",
223
+ type: "effect",
224
+ title: "Spotlight",
225
+ description: "Cursor-following spotlight effect.",
226
+ sourcePath: "registry/effects/spotlight",
227
+ dependencies: ["framer-motion"],
228
+ categories: ["effects", "interactive"]
229
+ }
230
+ ];
231
+ function getRegistryItem(name) {
232
+ return registry.find((item) => item.name === name);
233
+ }
234
+ function getRegistryItemsByType(type) {
235
+ return registry.filter((item) => item.type === type);
236
+ }
237
+ function getRegistryItemsByCategory(category) {
238
+ return registry.filter((item) => item.categories?.includes(category));
239
+ }
240
+
241
+ export { baseTokens, cn, getRegistryItem, getRegistryItemsByCategory, getRegistryItemsByType, registry, tailwindSemanticColors, useMotionPresets };
@@ -0,0 +1,9 @@
1
+ import { ClassValue } from 'clsx';
2
+
3
+ /**
4
+ * Merge Tailwind CSS classes with clsx and tailwind-merge.
5
+ * This ensures proper class merging and deduplication.
6
+ */
7
+ declare function cn(...inputs: ClassValue[]): string;
8
+
9
+ export { cn };
@@ -0,0 +1,9 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ // src/lib/utils.ts
5
+ function cn(...inputs) {
6
+ return twMerge(clsx(inputs));
7
+ }
8
+
9
+ export { cn };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Motion presets: use these across the app to keep motion consistent.
3
+ * Rule: do not hardcode durations/easing elsewhere.
4
+ */
5
+ type MotionPreset = {
6
+ initial?: Record<string, unknown>;
7
+ animate?: Record<string, unknown>;
8
+ exit?: Record<string, unknown>;
9
+ transition?: Record<string, unknown>;
10
+ };
11
+ declare function useMotionPresets(): {
12
+ fadeIn: MotionPreset;
13
+ slideUp: MotionPreset;
14
+ modalScale: MotionPreset;
15
+ stagger: {
16
+ container: MotionPreset;
17
+ item: MotionPreset;
18
+ };
19
+ meta: {
20
+ prefersReduced: boolean | null;
21
+ durations: {
22
+ fast: number;
23
+ base: number;
24
+ slow: number;
25
+ };
26
+ easing: {
27
+ standard: number[];
28
+ emphasized: number[];
29
+ };
30
+ };
31
+ };
32
+
33
+ export { type MotionPreset, useMotionPresets };
@@ -0,0 +1,59 @@
1
+ import { useReducedMotion } from 'framer-motion';
2
+
3
+ // src/motion/presets.ts
4
+ function useMotionPresets() {
5
+ const prefersReduced = useReducedMotion();
6
+ const durations = {
7
+ fast: 0.12,
8
+ base: 0.18,
9
+ slow: 0.26
10
+ };
11
+ const easing = {
12
+ standard: [0.2, 0, 0, 1],
13
+ emphasized: [0.2, 0.8, 0.2, 1]
14
+ };
15
+ const safe = (preset) => {
16
+ if (!prefersReduced) return preset;
17
+ return {
18
+ initial: { opacity: 0 },
19
+ animate: { opacity: 1 },
20
+ exit: { opacity: 0 },
21
+ transition: { duration: durations.fast }
22
+ };
23
+ };
24
+ return {
25
+ fadeIn: safe({
26
+ initial: { opacity: 0 },
27
+ animate: { opacity: 1 },
28
+ exit: { opacity: 0 },
29
+ transition: { duration: durations.base, ease: easing.standard }
30
+ }),
31
+ slideUp: safe({
32
+ initial: { opacity: 0, y: 10 },
33
+ animate: { opacity: 1, y: 0 },
34
+ exit: { opacity: 0, y: 6 },
35
+ transition: { duration: durations.base, ease: easing.standard }
36
+ }),
37
+ modalScale: safe({
38
+ initial: { opacity: 0, scale: 0.98 },
39
+ animate: { opacity: 1, scale: 1 },
40
+ exit: { opacity: 0, scale: 0.98 },
41
+ transition: { duration: durations.base, ease: easing.emphasized }
42
+ }),
43
+ stagger: {
44
+ container: safe({
45
+ initial: { opacity: 0 },
46
+ animate: { opacity: 1 },
47
+ transition: { staggerChildren: 0.06, delayChildren: 0.02 }
48
+ }),
49
+ item: safe({
50
+ initial: { opacity: 0, y: 8 },
51
+ animate: { opacity: 1, y: 0 },
52
+ transition: { duration: durations.base, ease: easing.standard }
53
+ })
54
+ },
55
+ meta: { prefersReduced, durations, easing }
56
+ };
57
+ }
58
+
59
+ export { useMotionPresets };
@@ -0,0 +1,17 @@
1
+ type RegistryItemType = "ui" | "block" | "effect" | "hook";
2
+ type RegistryItem = {
3
+ name: string;
4
+ type: RegistryItemType;
5
+ title: string;
6
+ description: string;
7
+ sourcePath: string;
8
+ dependencies?: string[];
9
+ registryDependencies?: string[];
10
+ categories?: string[];
11
+ };
12
+ declare const registry: RegistryItem[];
13
+ declare function getRegistryItem(name: string): RegistryItem | undefined;
14
+ declare function getRegistryItemsByType(type: RegistryItemType): RegistryItem[];
15
+ declare function getRegistryItemsByCategory(category: string): RegistryItem[];
16
+
17
+ export { type RegistryItem, type RegistryItemType, getRegistryItem, getRegistryItemsByCategory, getRegistryItemsByType, registry };