fancy-ui-svelte 0.8.0 → 0.9.1
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 +107 -89
- package/dist/fancy-ui/animated-testimonials/README.md +44 -0
- package/dist/fancy-ui/animated-tooltip/README.md +40 -0
- package/dist/fancy-ui/apple-card-carousel/README.md +46 -0
- package/dist/fancy-ui/bento-grid/BentoGrid.test.d.ts +1 -0
- package/dist/fancy-ui/bento-grid/BentoGrid.test.js +109 -0
- package/dist/fancy-ui/border-beam/README.md +37 -0
- package/dist/fancy-ui/card-3d/CardContainer.test.d.ts +1 -0
- package/dist/fancy-ui/card-3d/CardContainer.test.js +118 -0
- package/dist/fancy-ui/card-spotlight/CardSpotlight.test.d.ts +1 -0
- package/dist/fancy-ui/card-spotlight/CardSpotlight.test.js +58 -0
- package/dist/fancy-ui/displacement-text/README.md +39 -0
- package/dist/fancy-ui/dock/README.md +1 -1
- package/dist/fancy-ui/editorial-engine/EditorialEngine.test.d.ts +1 -0
- package/dist/fancy-ui/editorial-engine/EditorialEngine.test.js +54 -0
- package/dist/fancy-ui/fireworks-hdr/FireworksHdr.svelte +629 -0
- package/dist/fancy-ui/fireworks-hdr/FireworksHdr.svelte.d.ts +56 -0
- package/dist/fancy-ui/fireworks-hdr/FireworksHdr.test.d.ts +1 -0
- package/dist/fancy-ui/fireworks-hdr/FireworksHdr.test.js +74 -0
- package/dist/fancy-ui/fireworks-hdr/README.md +492 -0
- package/dist/fancy-ui/fireworks-hdr/fireworks-shared.d.ts +444 -0
- package/dist/fancy-ui/fireworks-hdr/fireworks-shared.js +1520 -0
- package/dist/fancy-ui/fireworks-hdr/fireworks-shared.test.d.ts +1 -0
- package/dist/fancy-ui/fireworks-hdr/fireworks-shared.test.js +1054 -0
- package/dist/fancy-ui/fireworks-hdr/index.d.ts +5 -0
- package/dist/fancy-ui/fireworks-hdr/index.js +5 -0
- package/dist/fancy-ui/fireworks-hdr/webgl2-renderer.d.ts +20 -0
- package/dist/fancy-ui/fireworks-hdr/webgl2-renderer.js +634 -0
- package/dist/fancy-ui/fireworks-hdr/webgl2-renderer.test.d.ts +1 -0
- package/dist/fancy-ui/fireworks-hdr/webgl2-renderer.test.js +33 -0
- package/dist/fancy-ui/fireworks-hdr/webgpu-renderer.d.ts +51 -0
- package/dist/fancy-ui/fireworks-hdr/webgpu-renderer.js +538 -0
- package/dist/fancy-ui/fluid-cursor/FluidCursor.svelte +130 -9
- package/dist/fancy-ui/fluid-cursor/FluidCursor.svelte.d.ts +9 -1
- package/dist/fancy-ui/fluid-cursor/FluidCursor.test.js +19 -0
- package/dist/fancy-ui/fluid-cursor/fluid-shared.d.ts +32 -0
- package/dist/fancy-ui/fluid-cursor/index.d.ts +1 -0
- package/dist/fancy-ui/fluid-cursor/webgpu-engine.d.ts +11 -4
- package/dist/fancy-ui/fluid-cursor/webgpu-engine.js +113 -34
- package/dist/fancy-ui/index.d.ts +1 -0
- package/dist/fancy-ui/index.js +1 -0
- package/dist/fancy-ui/line-hover-link/README.md +37 -0
- package/dist/fancy-ui/line-reveal/LineReveal.test.d.ts +1 -0
- package/dist/fancy-ui/line-reveal/LineReveal.test.js +77 -0
- package/dist/fancy-ui/marquee/README.md +57 -0
- package/dist/fancy-ui/matrix-rain/MatrixRain.test.d.ts +1 -0
- package/dist/fancy-ui/matrix-rain/MatrixRain.test.js +30 -0
- package/dist/fancy-ui/matrix-rain/README.md +42 -0
- package/dist/fancy-ui/noise-reveal/README.md +52 -0
- package/dist/fancy-ui/rainbow-button/README.md +36 -0
- package/dist/fancy-ui/registry.js +99 -20
- package/dist/fancy-ui/ripple-button/README.md +36 -0
- package/dist/fancy-ui/smooth-cursor/SmoothCursor.test.d.ts +1 -0
- package/dist/fancy-ui/smooth-cursor/SmoothCursor.test.js +45 -0
- package/dist/fancy-ui/terminal-text/README.md +48 -0
- package/dist/fancy-ui/terminal-text/TerminalText.test.d.ts +1 -0
- package/dist/fancy-ui/terminal-text/TerminalText.test.js +88 -0
- package/package.json +15 -2
package/README.md
CHANGED
|
@@ -5,12 +5,21 @@ Beautiful animation and UI components for **Svelte 5**.
|
|
|
5
5
|

|
|
6
6
|

|
|
7
7
|

|
|
8
|
-

|
|
9
9
|

|
|
10
10
|
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.anthropic.com/claude-code">
|
|
13
|
+
<picture>
|
|
14
|
+
<source media="(prefers-color-scheme: dark)" srcset=".github/claude-for-open-source-program-dark.svg">
|
|
15
|
+
<img src=".github/claude-for-open-source-program.svg" alt="Claude for Open Source Program" width="280">
|
|
16
|
+
</picture>
|
|
17
|
+
</a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
11
20
|
<p align="center">
|
|
12
21
|
<a href="https://fancy-ui.rama.app">
|
|
13
|
-
<img src=".github/
|
|
22
|
+
<img src=".github/fancyui-readme.png" alt="FancyUI preview" width="800" />
|
|
14
23
|
</a>
|
|
15
24
|
</p>
|
|
16
25
|
|
|
@@ -23,96 +32,107 @@ Beautiful animation and UI components for **Svelte 5**.
|
|
|
23
32
|
- **Svelte 5 Runes** — Built with `$state`, `$derived`, `$effect`, and `$props`
|
|
24
33
|
- **Tailwind CSS 4** — Utility-first styling with theme tokens
|
|
25
34
|
- **TypeScript** — Fully typed props and events
|
|
26
|
-
- **
|
|
35
|
+
- **61 Components** — Buttons, text animations, backgrounds, effects, and more
|
|
27
36
|
- **Dark Mode** — All components support light and dark themes
|
|
28
|
-
- **Tested** —
|
|
37
|
+
- **Tested** — 600+ unit tests with Vitest and Testing Library
|
|
29
38
|
|
|
30
39
|
## Components
|
|
31
40
|
|
|
32
41
|
### Buttons
|
|
33
42
|
|
|
34
|
-
| Component | Description | Demo
|
|
35
|
-
| ---------------------- | ---------------------------------------- |
|
|
36
|
-
| RainbowButton | Animated rainbow gradient border effect | [Demo](https://fancy-ui.rama.app/
|
|
37
|
-
| RippleButton | Ripple click effect | [Demo](https://fancy-ui.rama.app/
|
|
38
|
-
| ShimmerButton | Rotating conic-gradient shimmer border | [Demo](https://fancy-ui.rama.app/
|
|
39
|
-
| GradientButton | Rotating conic-gradient rainbow border | [Demo](https://fancy-ui.rama.app/
|
|
40
|
-
| InteractiveHoverButton | Hover effect revealing alternate content | [Demo](https://fancy-ui.rama.app/
|
|
43
|
+
| Component | Description | Demo |
|
|
44
|
+
| ---------------------- | ---------------------------------------- | -------------------------------------------------------------------------- |
|
|
45
|
+
| RainbowButton | Animated rainbow gradient border effect | [Demo](https://fancy-ui.rama.app/docs/components/rainbow-button) |
|
|
46
|
+
| RippleButton | Ripple click effect | [Demo](https://fancy-ui.rama.app/docs/components/ripple-button) |
|
|
47
|
+
| ShimmerButton | Rotating conic-gradient shimmer border | [Demo](https://fancy-ui.rama.app/docs/components/shimmer-button) |
|
|
48
|
+
| GradientButton | Rotating conic-gradient rainbow border | [Demo](https://fancy-ui.rama.app/docs/components/gradient-button) |
|
|
49
|
+
| InteractiveHoverButton | Hover effect revealing alternate content | [Demo](https://fancy-ui.rama.app/docs/components/interactive-hover-button) |
|
|
41
50
|
|
|
42
51
|
### Cards
|
|
43
52
|
|
|
44
|
-
| Component | Description
|
|
45
|
-
| ------------------- |
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
51
|
-
|
|
|
53
|
+
| Component | Description | Demo |
|
|
54
|
+
| ------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
55
|
+
| AppleCardCarousel | Horizontal card carousel with spring-animated full-screen expansion | [Demo](https://fancy-ui.rama.app/docs/components/apple-card-carousel) |
|
|
56
|
+
| Card3D | 3D tilt card with mouse tracking | [Demo](https://fancy-ui.rama.app/docs/components/card-3d) |
|
|
57
|
+
| CardSpotlight | Spotlight effect following mouse | [Demo](https://fancy-ui.rama.app/docs/components/card-spotlight) |
|
|
58
|
+
| DirectionAwareHover | Overlay slides in from mouse entry direction | [Demo](https://fancy-ui.rama.app/docs/components/direction-aware-hover) |
|
|
59
|
+
| FlipCard | Two-sided card with flip animation | [Demo](https://fancy-ui.rama.app/docs/components/flip-card) |
|
|
60
|
+
| GlareCard | Glare reflection effect on hover | [Demo](https://fancy-ui.rama.app/docs/components/glare-card) |
|
|
61
|
+
| TextRevealCard | Text reveal on hover | [Demo](https://fancy-ui.rama.app/docs/components/text-reveal-card) |
|
|
52
62
|
|
|
53
63
|
### Text & Typography
|
|
54
64
|
|
|
55
|
-
| Component | Description
|
|
56
|
-
| ------------------ |
|
|
57
|
-
| BlurReveal | Scroll-triggered blur-to-clear reveal
|
|
58
|
-
| BoxReveal | Sliding colored box reveal animation
|
|
59
|
-
| ColourfulText | Per-character color animation
|
|
60
|
-
| ContainerTextFlip | Text flipping inside a container
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
65
|
+
| Component | Description | Demo |
|
|
66
|
+
| ------------------ | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
|
|
67
|
+
| BlurReveal | Scroll-triggered blur-to-clear reveal | [Demo](https://fancy-ui.rama.app/docs/components/blur-reveal) |
|
|
68
|
+
| BoxReveal | Sliding colored box reveal animation | [Demo](https://fancy-ui.rama.app/docs/components/box-reveal) |
|
|
69
|
+
| ColourfulText | Per-character color animation | [Demo](https://fancy-ui.rama.app/docs/components/colourful-text) |
|
|
70
|
+
| ContainerTextFlip | Text flipping inside a container | [Demo](https://fancy-ui.rama.app/docs/components/container-text-flip) |
|
|
71
|
+
| DisplacementText | 3D text with WebGL displacement that follows the cursor using Three.js shaders | [Demo](https://fancy-ui.rama.app/docs/components/displacement-text) |
|
|
72
|
+
| EditorialEngine | Live magazine layout with multi-column text flowing around draggable orbs | [Demo](https://fancy-ui.rama.app/docs/components/editorial-engine) |
|
|
73
|
+
| FlipWords | Cycling word animation with per-letter effects | [Demo](https://fancy-ui.rama.app/docs/components/flip-words) |
|
|
74
|
+
| HyperText | Character scramble on hover | [Demo](https://fancy-ui.rama.app/docs/components/hyper-text) |
|
|
75
|
+
| LetterPullup | Staggered letter pull-up animation | [Demo](https://fancy-ui.rama.app/docs/components/letter-pullup) |
|
|
76
|
+
| LineReveal | Staggered line-by-line text reveal | [Demo](https://fancy-ui.rama.app/docs/components/line-reveal) |
|
|
77
|
+
| LineShadowText | Text with animated line shadow | [Demo](https://fancy-ui.rama.app/docs/components/line-shadow-text) |
|
|
78
|
+
| LiquidText | Big text that liquefies along the cursor's path via a WebGL fluid solver | [Demo](https://fancy-ui.rama.app/docs/components/liquid-text) |
|
|
79
|
+
| NumberTicker | Animated number counter with easing | [Demo](https://fancy-ui.rama.app/docs/components/number-ticker) |
|
|
80
|
+
| SparklesText | Animated SVG sparkle stars overlay | [Demo](https://fancy-ui.rama.app/docs/components/sparkles-text) |
|
|
81
|
+
| TerminalText | Terminal-style text streamer with blinking cursor and glitch effect | [Demo](https://fancy-ui.rama.app/docs/components/terminal-text) |
|
|
82
|
+
| TextGenerateEffect | Word-by-word text generation effect | [Demo](https://fancy-ui.rama.app/docs/components/text-generate-effect) |
|
|
68
83
|
|
|
69
84
|
### Backgrounds
|
|
70
85
|
|
|
71
|
-
| Component | Description
|
|
72
|
-
| ---------------------- |
|
|
73
|
-
| FallingStarsBg | Canvas 3D starfield with motion trails
|
|
74
|
-
| FlickeringGrid | Canvas grid with flickering opacity
|
|
75
|
-
| InteractiveGridPattern | SVG grid with hover highlights
|
|
76
|
-
|
|
|
86
|
+
| Component | Description | Demo |
|
|
87
|
+
| ---------------------- | --------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
88
|
+
| FallingStarsBg | Canvas 3D starfield with motion trails | [Demo](https://fancy-ui.rama.app/docs/components/bg-falling-stars) |
|
|
89
|
+
| FlickeringGrid | Canvas grid with flickering opacity | [Demo](https://fancy-ui.rama.app/docs/components/flickering-grid) |
|
|
90
|
+
| InteractiveGridPattern | SVG grid with hover highlights | [Demo](https://fancy-ui.rama.app/docs/components/interactive-grid-pattern) |
|
|
91
|
+
| MatrixRain | Canvas-based falling glyph rain with configurable color, speed, and density | [Demo](https://fancy-ui.rama.app/docs/components/matrix-rain) |
|
|
92
|
+
| StarsBackground | Starfield with parallax mouse tracking | [Demo](https://fancy-ui.rama.app/docs/components/bg-stars) |
|
|
77
93
|
|
|
78
94
|
### Effects & Animations
|
|
79
95
|
|
|
80
|
-
| Component | Description
|
|
81
|
-
| ---------------- |
|
|
82
|
-
| AnimatedBeam | SVG beams connecting elements
|
|
83
|
-
| BorderBeam | Beam effect traveling around borders
|
|
84
|
-
| Confetti | Configurable confetti burst
|
|
85
|
-
| FluidCursor | WebGL fluid simulation following cursor
|
|
86
|
-
|
|
|
87
|
-
|
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
|
|
|
96
|
+
| Component | Description | Demo |
|
|
97
|
+
| ---------------- | -------------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
98
|
+
| AnimatedBeam | SVG beams connecting elements | [Demo](https://fancy-ui.rama.app/docs/components/animated-beam) |
|
|
99
|
+
| BorderBeam | Beam effect traveling around borders | [Demo](https://fancy-ui.rama.app/docs/components/border-beam) |
|
|
100
|
+
| Confetti | Configurable confetti burst | [Demo](https://fancy-ui.rama.app/docs/components/confetti) |
|
|
101
|
+
| FluidCursor | WebGL fluid simulation following cursor | [Demo](https://fancy-ui.rama.app/docs/components/fluid-cursor) |
|
|
102
|
+
| FrostedGlass | Frosted glass surface with organic turbulence-noise refraction | [Demo](https://fancy-ui.rama.app/docs/components/frosted-glass) |
|
|
103
|
+
| GlowBorder | Animated glowing border with gradients | [Demo](https://fancy-ui.rama.app/docs/components/glow-border) |
|
|
104
|
+
| GlowingEffect | Glowing highlight on hover | [Demo](https://fancy-ui.rama.app/docs/components/glowing-effect) |
|
|
105
|
+
| ImageTrailCursor | Cursor-following image trail (8 variants) | [Demo](https://fancy-ui.rama.app/docs/components/image-trail-cursor) |
|
|
106
|
+
| LiquidGlass | Liquid glass morphism effect | [Demo](https://fancy-ui.rama.app/docs/components/liquid-glass) |
|
|
107
|
+
| Meteors | Animated meteor shower effect | [Demo](https://fancy-ui.rama.app/docs/components/meteors) |
|
|
108
|
+
| NeonBorder | Dual-color neon glow border | [Demo](https://fancy-ui.rama.app/docs/components/neon-border) |
|
|
109
|
+
| Ripple | Expanding ripple rings | [Demo](https://fancy-ui.rama.app/docs/components/ripple) |
|
|
110
|
+
| SmoothCursor | Smooth lagging cursor follower | [Demo](https://fancy-ui.rama.app/docs/components/smooth-cursor) |
|
|
111
|
+
| Sparkles | Particle sparkle canvas | [Demo](https://fancy-ui.rama.app/docs/components/sparkles) |
|
|
112
|
+
| TracingBeam | Scroll-driven tracing beam | [Demo](https://fancy-ui.rama.app/docs/components/tracing-beam) |
|
|
96
113
|
|
|
97
114
|
### Layout
|
|
98
115
|
|
|
99
|
-
| Component | Description | Demo
|
|
100
|
-
| --------------- | --------------------------------------------- |
|
|
101
|
-
| BentoGrid | Bento-style responsive grid layout | [Demo](https://fancy-ui.rama.app/
|
|
102
|
-
| Book | 3D book flip animation | [Demo](https://fancy-ui.rama.app/
|
|
103
|
-
| ContainerScroll | 3D scroll perspective container | [Demo](https://fancy-ui.rama.app/
|
|
104
|
-
| Focus | Focus-expand card layout | [Demo](https://fancy-ui.rama.app/
|
|
105
|
-
| Marquee | Infinite scrolling for text, images, or cards | [Demo](https://fancy-ui.rama.app/
|
|
116
|
+
| Component | Description | Demo |
|
|
117
|
+
| --------------- | --------------------------------------------- | ------------------------------------------------------------------ |
|
|
118
|
+
| BentoGrid | Bento-style responsive grid layout | [Demo](https://fancy-ui.rama.app/docs/components/bento-grid) |
|
|
119
|
+
| Book | 3D book flip animation | [Demo](https://fancy-ui.rama.app/docs/components/book) |
|
|
120
|
+
| ContainerScroll | 3D scroll perspective container | [Demo](https://fancy-ui.rama.app/docs/components/container-scroll) |
|
|
121
|
+
| Focus | Focus-expand card layout | [Demo](https://fancy-ui.rama.app/docs/components/focus) |
|
|
122
|
+
| Marquee | Infinite scrolling for text, images, or cards | [Demo](https://fancy-ui.rama.app/docs/components/marquee) |
|
|
106
123
|
|
|
107
124
|
### Navigation & Display
|
|
108
125
|
|
|
109
|
-
| Component
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
|
|
|
114
|
-
|
|
|
115
|
-
|
|
|
126
|
+
| Component | Description | Demo |
|
|
127
|
+
| -------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
128
|
+
| AnimatedTestimonials | Testimonial carousel with smooth slide animations and optional autoplay | [Demo](https://fancy-ui.rama.app/docs/components/animated-testimonials) |
|
|
129
|
+
| AnimatedTooltip | Avatar row with mouse-following tooltips | [Demo](https://fancy-ui.rama.app/docs/components/animated-tooltip) |
|
|
130
|
+
| Compare | Before/after image comparison slider | [Demo](https://fancy-ui.rama.app/docs/components/compare) |
|
|
131
|
+
| Dock | Icon dock where items magnify as the cursor approaches | [Demo](https://fancy-ui.rama.app/docs/components/dock) |
|
|
132
|
+
| LineHoverLink | Link with 11 animated underline hover effects, pure CSS | [Demo](https://fancy-ui.rama.app/docs/components/line-hover-link) |
|
|
133
|
+
| LogoCloud | Marquee, grid, and icon logo layouts | [Demo](https://fancy-ui.rama.app/docs/components/logo-cloud) |
|
|
134
|
+
| NoiseReveal | WebGL image reveal with a Perlin-noise dissolve mask | [Demo](https://fancy-ui.rama.app/docs/components/noise-reveal) |
|
|
135
|
+
| Timeline | Vertical timeline with scroll-driven progress | [Demo](https://fancy-ui.rama.app/docs/components/timeline) |
|
|
116
136
|
|
|
117
137
|
## Quick Start
|
|
118
138
|
|
|
@@ -186,12 +206,14 @@ SmoothCursor) mount once in the root +layout.svelte; Svelte 5 syntax only.
|
|
|
186
206
|
## Development
|
|
187
207
|
|
|
188
208
|
```bash
|
|
189
|
-
pnpm dev
|
|
190
|
-
pnpm check
|
|
191
|
-
pnpm
|
|
192
|
-
pnpm
|
|
193
|
-
pnpm
|
|
194
|
-
pnpm
|
|
209
|
+
pnpm dev # Start dev server
|
|
210
|
+
pnpm check # Run Svelte type checker
|
|
211
|
+
pnpm check:registry # Verify component registry parity (CI gate)
|
|
212
|
+
pnpm check:i18n # Verify i18n message catalog parity (CI gate)
|
|
213
|
+
pnpm test # Run tests
|
|
214
|
+
pnpm test:watch # Run tests in watch mode
|
|
215
|
+
pnpm build # Production build
|
|
216
|
+
pnpm storybook # Component workshop on :6006
|
|
195
217
|
```
|
|
196
218
|
|
|
197
219
|
Component stories live in `src/stories/` — see the [Storybook section in CONTRIBUTING.md](./CONTRIBUTING.md#storybook).
|
|
@@ -210,22 +232,26 @@ src/
|
|
|
210
232
|
│ │ ├── registry.ts # Component registry & metadata
|
|
211
233
|
│ │ └── index.ts # Barrel exports
|
|
212
234
|
│ └── components/ui/ # shadcn-svelte primitives
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
235
|
+
└── routes/
|
|
236
|
+
├── +page.svelte # Home page
|
|
237
|
+
└── docs/ # Docs site (registry-driven component pages)
|
|
238
|
+
└── components/[slug]/+page.svelte
|
|
239
|
+
|
|
240
|
+
tests/
|
|
241
|
+
└── e2e/ # Playwright end-to-end tests
|
|
218
242
|
```
|
|
219
243
|
|
|
244
|
+
Component tests are colocated with their component (see `RainbowButton.test.ts` above); there is no separate top-level unit-test directory.
|
|
245
|
+
|
|
220
246
|
## Contributing
|
|
221
247
|
|
|
222
|
-
Contributions are welcome!
|
|
248
|
+
Contributions are welcome! 61 components and counting — PRs for new components, bug fixes, and improvements are appreciated.
|
|
223
249
|
|
|
224
250
|
### Adding a new component
|
|
225
251
|
|
|
226
252
|
1. Create the component folder under `src/lib/fancy-ui/`
|
|
227
253
|
2. Implement the component in idiomatic Svelte 5
|
|
228
|
-
3. Add a
|
|
254
|
+
3. Add a docs example under `src/lib/components/docs/examples/<slug>/` (`BasicUsage.svelte`)
|
|
229
255
|
4. Register it in `src/lib/fancy-ui/registry.ts`
|
|
230
256
|
5. Export it from `src/lib/fancy-ui/index.ts`
|
|
231
257
|
|
|
@@ -248,11 +274,3 @@ Inspired by [Inspira UI](https://inspira-ui.com), [Aceternity UI](https://ui.ace
|
|
|
248
274
|
## License
|
|
249
275
|
|
|
250
276
|
MIT
|
|
251
|
-
|
|
252
|
-
---
|
|
253
|
-
|
|
254
|
-
<p align="center">
|
|
255
|
-
<a href="https://www.anthropic.com/claude-code">
|
|
256
|
-
<img src=".github/claude-code-open-source-program.svg" alt="Claude Code Open Source Program" width="420" />
|
|
257
|
-
</a>
|
|
258
|
-
</p>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# AnimatedTestimonials
|
|
2
|
+
|
|
3
|
+
A testimonial carousel that cross-fades between quote/author pairs, with a matching stack of avatar images that slide and rotate in behind it. Supports manual prev/next navigation and optional autoplay that pauses on hover.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```svelte
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import { AnimatedTestimonials } from "fancy-ui-svelte";
|
|
10
|
+
import type { Testimonial } from "fancy-ui-svelte";
|
|
11
|
+
|
|
12
|
+
const testimonials: Testimonial[] = [
|
|
13
|
+
{
|
|
14
|
+
quote:
|
|
15
|
+
"The attention to detail and innovative features have completely transformed our workflow.",
|
|
16
|
+
name: "Sarah Chen",
|
|
17
|
+
designation: "Product Manager at TechFlow",
|
|
18
|
+
src: "https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=500",
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<AnimatedTestimonials {testimonials} autoplay interval={4000} />
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Props
|
|
27
|
+
|
|
28
|
+
| Prop | Type | Default | Description |
|
|
29
|
+
| -------------- | --------------- | ------- | ------------------------------------------- |
|
|
30
|
+
| `testimonials` | `Testimonial[]` | — | Array of testimonials to display (required) |
|
|
31
|
+
| `autoplay` | `boolean` | `false` | Auto-advance testimonials |
|
|
32
|
+
| `interval` | `number` | `5000` | Interval between auto-advances (ms) |
|
|
33
|
+
| `class` | `string` | — | Additional CSS classes on the root |
|
|
34
|
+
|
|
35
|
+
`Testimonial` shape: `{ quote: string; name: string; designation: string; src: string }` — `src` is the author's avatar image URL.
|
|
36
|
+
|
|
37
|
+
## Implementation notes
|
|
38
|
+
|
|
39
|
+
- `navigate()` is guarded by an `isAnimating` flag so rapid clicks or a running autoplay timer can't overlap two transitions; a new navigation is dropped, not queued, while one is in flight.
|
|
40
|
+
- Autoplay is a single `$effect` that starts a `setInterval` whenever `autoplay`, `interval`, or hover state changes, and returns its own cleanup — hovering the container (`onmouseenter`/`onmouseleave`) pauses it without a separate timer to manage.
|
|
41
|
+
- The image stack renders every testimonial absolutely-positioned and stacked (`z-20`/`z-10`), swapping visibility and transform per index rather than mounting/unmounting nodes — this is what makes the slide-in-behind effect possible without a Svelte transition.
|
|
42
|
+
- The image and text transition durations are both tied to the same `TRANSITION_DURATION` module constant (300ms); it isn't exposed as a prop, so it must be edited in the component source if a different pace is needed.
|
|
43
|
+
- Renders a "No testimonials available." message instead of the carousel when `testimonials` is empty, rather than crashing on an out-of-bounds `activeIndex`.
|
|
44
|
+
- No `prefers-reduced-motion` handling — the slide/scale/opacity transitions run regardless of user preference.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# AnimatedTooltip
|
|
2
|
+
|
|
3
|
+
A row of overlapping avatars that pop a name/role tooltip above the hovered item. The tooltip tilts and shifts horizontally based on where the cursor is over the avatar, giving it a light spring-like feel without a physics library.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```svelte
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import { AnimatedTooltip } from "fancy-ui-svelte";
|
|
10
|
+
import type { TooltipItem } from "fancy-ui-svelte";
|
|
11
|
+
|
|
12
|
+
const people: TooltipItem[] = [
|
|
13
|
+
{
|
|
14
|
+
id: 1,
|
|
15
|
+
name: "John Doe",
|
|
16
|
+
designation: "Software Engineer",
|
|
17
|
+
image: "https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100&h=100&fit=crop",
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<AnimatedTooltip items={people} />
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Props
|
|
26
|
+
|
|
27
|
+
| Prop | Type | Default | Description |
|
|
28
|
+
| ------- | --------------- | ------- | --------------------------------------- |
|
|
29
|
+
| `items` | `TooltipItem[]` | — | Array of items to display (required) |
|
|
30
|
+
| `class` | `string` | — | Additional CSS classes on the container |
|
|
31
|
+
|
|
32
|
+
`TooltipItem` shape: `{ id: number | string; name: string; designation: string; image: string }`.
|
|
33
|
+
|
|
34
|
+
## Implementation notes
|
|
35
|
+
|
|
36
|
+
- Rotation and horizontal translation are both derived from the same value: `mouseX`, the pointer's offset from the hovered avatar's horizontal center, scaled by `/100 * 50`. One `$derived` value drives both the `rotate()` and the `translateX()` in the tooltip's inline `style`.
|
|
37
|
+
- `mouseX` is recalculated from scratch on `mouseenter` (not carried over from the previous avatar) to avoid a jump when the pointer moves between adjacent, overlapping avatars.
|
|
38
|
+
- The tooltip is conditionally rendered (`{#if hoveredIndex === item.id}`) rather than always mounted and toggled with opacity, so it uses Svelte's `transition:scale` (200ms, `start: 0.6`) on enter/exit instead of a CSS-only transition.
|
|
39
|
+
- Avatars overlap via a `-mr-4` negative margin on each item and `group-hover:z-30`/`scale-105` to lift the hovered one above its neighbors.
|
|
40
|
+
- No `prefers-reduced-motion` handling — the scale transition and position/rotation tracking run regardless of user preference.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# AppleCardCarousel
|
|
2
|
+
|
|
3
|
+
A horizontally scroll-snapping card carousel where tapping a card grows it from its on-screen position into a full-screen modal, then shrinks it back to that same spot on close — the geometry is measured, not animated between two fixed states.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```svelte
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import { AppleCardCarousel } from "fancy-ui-svelte";
|
|
10
|
+
import type { AppleCardData } from "fancy-ui-svelte";
|
|
11
|
+
|
|
12
|
+
const cards: AppleCardData[] = [
|
|
13
|
+
{
|
|
14
|
+
category: "Nature",
|
|
15
|
+
title: "Misty Mountains",
|
|
16
|
+
src: "https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800",
|
|
17
|
+
description: "A breathtaking view of misty mountains at dawn.",
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
</script>
|
|
21
|
+
|
|
22
|
+
<AppleCardCarousel {cards} />
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Props
|
|
26
|
+
|
|
27
|
+
### AppleCardCarousel
|
|
28
|
+
|
|
29
|
+
| Prop | Type | Default | Description |
|
|
30
|
+
| ------- | ----------------- | ------- | ---------------------------------- |
|
|
31
|
+
| `cards` | `AppleCardData[]` | — | Cards to display (required) |
|
|
32
|
+
| `class` | `string` | `""` | Additional CSS classes on the root |
|
|
33
|
+
|
|
34
|
+
`AppleCardData` shape: `{ category: string; title: string; src: string; description?: string; content?: Snippet }`. `content`, when provided, replaces `description` in the expanded view — pass a Svelte snippet for rich content (headings, lists, embedded components) instead of a plain paragraph.
|
|
35
|
+
|
|
36
|
+
`AppleCard` is also exported directly (`import { AppleCard } from "fancy-ui-svelte"`) for building a custom carousel shell around the individual expand/collapse card, but its own prop interface is internal to the component and not re-exported as a named type.
|
|
37
|
+
|
|
38
|
+
## Implementation notes
|
|
39
|
+
|
|
40
|
+
- Only one card can be expanded at a time: `expandedIndex` lives in `AppleCardCarousel` (not in each `AppleCard`) and is passed down along with `onExpand`/`onCollapse` callbacks, so a card whose `handleExpand()` fires while another is already open is a no-op (`if (expandedIndex !== -1) return`).
|
|
41
|
+
- The expand animation is a FLIP-style measure-then-morph: on click, `AppleCard` reads the collapsed card's `getBoundingClientRect()` and freezes it as the overlay's starting `top`/`left`/`width`/`height`, then flips a `fullyExpanded` flag (via a double `requestAnimationFrame` to guarantee a layout pass) that CSS-transitions those same properties to full-viewport.
|
|
42
|
+
- `prefers-reduced-motion` is detected once via `matchMedia` in `AppleCardCarousel` (with a `change` listener, so it updates if the user flips the OS setting mid-session) and passed down as `reducedMotion`. When true, `AppleCard` skips the double-rAF and the geometry transition duration drops to `0`.
|
|
43
|
+
- Focus management: opening a card saves `document.activeElement`, moves focus to the modal's close button, and traps `Tab`/`Shift+Tab` inside the dialog by walking its focusable elements; closing restores focus to whatever had it before.
|
|
44
|
+
- `Escape` closes the expanded card from anywhere inside the dialog (`onkeydown` on the dialog root).
|
|
45
|
+
- Collapse is deferred: the overlay stays mounted for `TRANSITION_MS` (400ms, or `0` under reduced motion) after `fullyExpanded` flips back to `false`, so the shrink transition can play before the DOM node is removed and `onCollapse` fires.
|
|
46
|
+
- The carousel track uses native scroll-snap (`snap-x snap-mandatory`) with the scrollbar hidden via `[scrollbar-width:none]` / `[&::-webkit-scrollbar]:hidden`, not a JS-driven scroller.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { render, cleanup } from "@testing-library/svelte";
|
|
2
|
+
import { createRawSnippet } from "svelte";
|
|
3
|
+
import { afterEach, describe, it, expect } from "vitest";
|
|
4
|
+
import BentoGrid from "./BentoGrid.svelte";
|
|
5
|
+
import BentoGridItem from "./BentoGridItem.svelte";
|
|
6
|
+
import BentoGridCard from "./BentoGridCard.svelte";
|
|
7
|
+
function textSnippet(html) {
|
|
8
|
+
return createRawSnippet(() => ({ render: () => html }));
|
|
9
|
+
}
|
|
10
|
+
describe("BentoGrid", () => {
|
|
11
|
+
afterEach(cleanup);
|
|
12
|
+
it("renders a grid wrapper with the expected layout classes", () => {
|
|
13
|
+
const { container } = render(BentoGrid);
|
|
14
|
+
const grid = container.firstElementChild;
|
|
15
|
+
expect(grid.className).toContain("grid");
|
|
16
|
+
expect(grid.className).toContain("md:grid-cols-3");
|
|
17
|
+
});
|
|
18
|
+
it("applies custom class names alongside the base layout classes", () => {
|
|
19
|
+
const { container } = render(BentoGrid, { props: { class: "my-grid" } });
|
|
20
|
+
const grid = container.firstElementChild;
|
|
21
|
+
expect(grid.className).toContain("my-grid");
|
|
22
|
+
expect(grid.className).toContain("grid");
|
|
23
|
+
});
|
|
24
|
+
it("renders children content passed via the default snippet", () => {
|
|
25
|
+
const { container } = render(BentoGrid, {
|
|
26
|
+
props: { children: textSnippet("<span data-testid='child'>card</span>") },
|
|
27
|
+
});
|
|
28
|
+
expect(container.querySelector("[data-testid='child']")?.textContent).toBe("card");
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe("BentoGridItem", () => {
|
|
32
|
+
afterEach(cleanup);
|
|
33
|
+
it("renders with the base bento-card classes", () => {
|
|
34
|
+
const { container } = render(BentoGridItem);
|
|
35
|
+
const item = container.firstElementChild;
|
|
36
|
+
expect(item.className).toContain("group/bento");
|
|
37
|
+
expect(item.className).toContain("rounded-xl");
|
|
38
|
+
});
|
|
39
|
+
it("applies custom class names", () => {
|
|
40
|
+
const { container } = render(BentoGridItem, { props: { class: "my-item" } });
|
|
41
|
+
const item = container.firstElementChild;
|
|
42
|
+
expect(item.className).toContain("my-item");
|
|
43
|
+
});
|
|
44
|
+
it("renders the title snippet", () => {
|
|
45
|
+
const { container } = render(BentoGridItem, {
|
|
46
|
+
props: { title: textSnippet("<span>Widget title</span>") },
|
|
47
|
+
});
|
|
48
|
+
expect(container.textContent).toContain("Widget title");
|
|
49
|
+
});
|
|
50
|
+
it("renders the description snippet", () => {
|
|
51
|
+
const { container } = render(BentoGridItem, {
|
|
52
|
+
props: { description: textSnippet("<span>Widget description</span>") },
|
|
53
|
+
});
|
|
54
|
+
expect(container.textContent).toContain("Widget description");
|
|
55
|
+
});
|
|
56
|
+
it("renders the header and icon snippets", () => {
|
|
57
|
+
const { container } = render(BentoGridItem, {
|
|
58
|
+
props: {
|
|
59
|
+
header: textSnippet("<div data-testid='header'>H</div>"),
|
|
60
|
+
icon: textSnippet("<svg data-testid='icon'></svg>"),
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
expect(container.querySelector("[data-testid='header']")).toBeTruthy();
|
|
64
|
+
expect(container.querySelector("[data-testid='icon']")).toBeTruthy();
|
|
65
|
+
});
|
|
66
|
+
it("omits the title/description wrappers when those snippets are not provided", () => {
|
|
67
|
+
const { container } = render(BentoGridItem);
|
|
68
|
+
// Only the outer card and the inner hover-translate wrapper should exist.
|
|
69
|
+
expect(container.querySelectorAll("div").length).toBe(2);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe("BentoGridCard", () => {
|
|
73
|
+
afterEach(cleanup);
|
|
74
|
+
const baseProps = {
|
|
75
|
+
name: "Feature",
|
|
76
|
+
description: "Does a thing",
|
|
77
|
+
href: "/feature",
|
|
78
|
+
cta: "Learn more",
|
|
79
|
+
};
|
|
80
|
+
it("renders the name, description, and cta link", () => {
|
|
81
|
+
const { container } = render(BentoGridCard, { props: baseProps });
|
|
82
|
+
const heading = container.querySelector("h3");
|
|
83
|
+
const desc = container.querySelector("p");
|
|
84
|
+
const link = container.querySelector("a");
|
|
85
|
+
expect(heading?.textContent?.trim()).toBe("Feature");
|
|
86
|
+
expect(desc?.textContent).toBe("Does a thing");
|
|
87
|
+
expect(link.getAttribute("href")).toBe("/feature");
|
|
88
|
+
expect(link.textContent).toContain("Learn more");
|
|
89
|
+
});
|
|
90
|
+
it("applies custom class names", () => {
|
|
91
|
+
const { container } = render(BentoGridCard, {
|
|
92
|
+
props: { ...baseProps, class: "my-card" },
|
|
93
|
+
});
|
|
94
|
+
const card = container.firstElementChild;
|
|
95
|
+
expect(card.className).toContain("my-card");
|
|
96
|
+
});
|
|
97
|
+
it("renders an icon snippet when provided", () => {
|
|
98
|
+
const { container } = render(BentoGridCard, {
|
|
99
|
+
props: { ...baseProps, icon: textSnippet("<svg data-testid='icon'></svg>") },
|
|
100
|
+
});
|
|
101
|
+
expect(container.querySelector("[data-testid='icon']")).toBeTruthy();
|
|
102
|
+
});
|
|
103
|
+
it("renders a background snippet when provided", () => {
|
|
104
|
+
const { container } = render(BentoGridCard, {
|
|
105
|
+
props: { ...baseProps, background: textSnippet("<div data-testid='bg'></div>") },
|
|
106
|
+
});
|
|
107
|
+
expect(container.querySelector("[data-testid='bg']")).toBeTruthy();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# BorderBeam
|
|
2
|
+
|
|
3
|
+
An animated light beam that travels around a container's border using a CSS `offset-path` motion path — no JS, no SVG. Place it inside a `position: relative; overflow: hidden` container; it renders as an absolutely-positioned overlay that traces the container's edge.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```svelte
|
|
8
|
+
<script lang="ts">
|
|
9
|
+
import { BorderBeam } from "fancy-ui-svelte";
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<div class="relative overflow-hidden rounded-xl border p-6">
|
|
13
|
+
<p>Content goes here</p>
|
|
14
|
+
<BorderBeam />
|
|
15
|
+
</div>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Multiple beams can be layered in the same container (e.g. different `duration`/`delay`/colors per beam) for a multi-beam effect — see the `MultipleBeams` docs example.
|
|
19
|
+
|
|
20
|
+
## Props
|
|
21
|
+
|
|
22
|
+
| Prop | Type | Default | Description |
|
|
23
|
+
| ------------- | -------- | ----------- | --------------------------------------------- |
|
|
24
|
+
| `size` | `number` | `200` | Size of the beam in pixels |
|
|
25
|
+
| `duration` | `number` | `15` | Animation duration in seconds |
|
|
26
|
+
| `borderWidth` | `number` | `1.5` | Border width in pixels |
|
|
27
|
+
| `anchor` | `number` | `90` | Anchor position along the offset path (0–100) |
|
|
28
|
+
| `colorFrom` | `string` | `"#ffaa40"` | Gradient start color |
|
|
29
|
+
| `colorTo` | `string` | `"#9c40ff"` | Gradient end color |
|
|
30
|
+
| `delay` | `number` | `0` | Animation delay in seconds |
|
|
31
|
+
| `class` | `string` | — | Additional CSS classes |
|
|
32
|
+
|
|
33
|
+
## Implementation notes
|
|
34
|
+
|
|
35
|
+
- Every prop is a CSS custom property (`--border-beam-*`) written into the root `<div>`'s inline `style` via a single `$derived` string — the component holds no other reactive state, and the animation itself runs entirely in a scoped `<style>` block (`@keyframes border-beam-animation`, driven by `offset-distance`).
|
|
36
|
+
- The border itself is drawn with a transparent border plus a two-layer `mask` (`linear-gradient(transparent,transparent), linear-gradient(white,white)`) and `mask-composite: intersect`, so only the ring where the border would be is visible — the beam element's `::after` pseudo-element is what actually moves, via `offset-path: rect(...)` sized from `--border-beam-size`.
|
|
37
|
+
- No `prefers-reduced-motion` handling — the beam animates continuously (`infinite linear`) regardless of user preference.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|