fancy-ui-svelte 0.4.1 → 0.6.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.
Files changed (36) hide show
  1. package/README.md +40 -7
  2. package/dist/fancy-ui/dock/index.d.ts +1 -1
  3. package/dist/fancy-ui/dock/index.js +0 -1
  4. package/dist/fancy-ui/editorial-engine/EditorialEngine.svelte +181 -0
  5. package/dist/fancy-ui/editorial-engine/EditorialEngine.svelte.d.ts +26 -0
  6. package/dist/fancy-ui/editorial-engine/README.md +56 -0
  7. package/dist/fancy-ui/editorial-engine/engine.d.ts +7 -0
  8. package/dist/fancy-ui/editorial-engine/engine.js +581 -0
  9. package/dist/fancy-ui/editorial-engine/index.d.ts +2 -0
  10. package/dist/fancy-ui/editorial-engine/index.js +1 -0
  11. package/dist/fancy-ui/fluid-cursor/FluidCursor.svelte +168 -32
  12. package/dist/fancy-ui/fluid-cursor/FluidCursor.svelte.d.ts +7 -0
  13. package/dist/fancy-ui/fluid-cursor/FluidCursor.test.js +36 -9
  14. package/dist/fancy-ui/fluid-cursor/index.d.ts +4 -1
  15. package/dist/fancy-ui/fluid-cursor/index.js +4 -1
  16. package/dist/fancy-ui/index.d.ts +4 -0
  17. package/dist/fancy-ui/index.js +4 -0
  18. package/dist/fancy-ui/line-reveal/LineReveal.svelte +168 -0
  19. package/dist/fancy-ui/line-reveal/LineReveal.svelte.d.ts +33 -0
  20. package/dist/fancy-ui/line-reveal/README.md +42 -0
  21. package/dist/fancy-ui/line-reveal/index.d.ts +2 -0
  22. package/dist/fancy-ui/line-reveal/index.js +1 -0
  23. package/dist/fancy-ui/noise-reveal/NoiseReveal.svelte +323 -0
  24. package/dist/fancy-ui/noise-reveal/NoiseReveal.svelte.d.ts +19 -0
  25. package/dist/fancy-ui/noise-reveal/NoiseReveal.test.js +123 -0
  26. package/dist/fancy-ui/noise-reveal/index.d.ts +2 -0
  27. package/dist/fancy-ui/noise-reveal/index.js +1 -0
  28. package/dist/fancy-ui/registry.js +1589 -9
  29. package/dist/fancy-ui/shimmer-button/ShimmerButton.svelte +1 -1
  30. package/dist/fancy-ui/tailwind.css +2 -2
  31. package/dist/types.d.ts +37 -0
  32. package/package.json +10 -2
  33. package/dist/fancy-ui/fluid-cursor/FluidCursorAdvanced.svelte +0 -1556
  34. package/dist/fancy-ui/fluid-cursor/FluidCursorAdvanced.svelte.d.ts +0 -33
  35. package/dist/fancy-ui/fluid-cursor/FluidCursorAdvanced.test.js +0 -86
  36. /package/dist/fancy-ui/{fluid-cursor/FluidCursorAdvanced.test.d.ts → noise-reveal/NoiseReveal.test.d.ts} +0 -0
package/README.md CHANGED
@@ -5,7 +5,7 @@ Beautiful animation and UI components for **Svelte 5**.
5
5
  ![Svelte 5](https://img.shields.io/badge/Svelte-5-FF3E00?logo=svelte&logoColor=white)
6
6
  ![Tailwind CSS](https://img.shields.io/badge/Tailwind_CSS-4-06B6D4?logo=tailwindcss&logoColor=white)
7
7
  ![TypeScript](https://img.shields.io/badge/TypeScript-5-3178C6?logo=typescript&logoColor=white)
8
- ![Components](https://img.shields.io/badge/Components-52-8B5CF6)
8
+ ![Components](https://img.shields.io/badge/Components-56-8B5CF6)
9
9
  ![MIT License](https://img.shields.io/badge/License-MIT-green)
10
10
 
11
11
  <p align="center">
@@ -23,7 +23,7 @@ Beautiful animation and UI components for **Svelte 5**.
23
23
  - **Svelte 5 Runes** &mdash; Built with `$state`, `$derived`, `$effect`, and `$props`
24
24
  - **Tailwind CSS 4** &mdash; Utility-first styling with theme tokens
25
25
  - **TypeScript** &mdash; Fully typed props and events
26
- - **52 Components** &mdash; Buttons, text animations, backgrounds, effects, and more
26
+ - **56 Components** &mdash; Buttons, text animations, backgrounds, effects, and more
27
27
  - **Dark Mode** &mdash; All components support light and dark themes
28
28
  - **Tested** &mdash; Component tests with Vitest and Testing Library
29
29
 
@@ -119,21 +119,35 @@ Beautiful animation and UI components for **Svelte 5**.
119
119
  **Install via npm:**
120
120
 
121
121
  ```bash
122
- npm install fancy-ui
122
+ npm install fancy-ui-svelte
123
123
  ```
124
124
 
125
125
  Then import any component:
126
126
 
127
127
  ```ts
128
- import { Marquee, BorderBeam, Confetti } from 'fancy-ui';
128
+ import { Marquee, BorderBeam, Confetti } from 'fancy-ui-svelte';
129
129
  ```
130
130
 
131
- To include Tailwind classes, add this to your app's CSS:
131
+ Add the Tailwind integration to your app's CSS (e.g. `src/app.css`):
132
132
 
133
133
  ```css
134
- @import "fancy-ui/tailwind.css";
134
+ @import "tailwindcss";
135
+ @import "fancy-ui-svelte/tailwind.css";
135
136
  ```
136
137
 
138
+ This single import tells Tailwind v4 to scan the library's components so all utility classes are generated automatically. No manual `@source` path needed.
139
+
140
+ <details>
141
+ <summary>Alternative: manual @source (without the CSS import)</summary>
142
+
143
+ ```css
144
+ @import "tailwindcss";
145
+ @source "../node_modules/fancy-ui-svelte/dist";
146
+ ```
147
+
148
+ The path is relative to your CSS file location.
149
+ </details>
150
+
137
151
  **Or browse and copy a component:**
138
152
  1. Find the component you need in the [live demo](https://fancy-ui.rama.app)
139
153
  2. Copy the source from `src/lib/fancy-ui/[component-name]/`
@@ -148,6 +162,25 @@ pnpm install
148
162
  pnpm dev
149
163
  ```
150
164
 
165
+ ## Using with AI agents (Claude Code, Cursor, Copilot)
166
+
167
+ The docs site serves LLM-friendly documentation following the [llms.txt](https://llmstxt.org) convention:
168
+
169
+ - [fancy-ui.rama.app/llms.txt](https://fancy-ui.rama.app/llms.txt) — setup guide, usage rules, and component index
170
+ - [fancy-ui.rama.app/llms-full.txt](https://fancy-ui.rama.app/llms-full.txt) — full reference with every component's props
171
+
172
+ To make your coding agent use fancy-ui correctly, add this to your project's `CLAUDE.md`, `AGENTS.md`, or `.cursorrules`:
173
+
174
+ ```markdown
175
+ ## UI components
176
+
177
+ Use fancy-ui-svelte (Svelte 5 + Tailwind CSS v4) for animated UI components.
178
+ Full component and props reference: https://fancy-ui.rama.app/llms-full.txt
179
+ Key rules: overlay/effect components (BorderBeam, GlowBorder, backgrounds, ...)
180
+ need a parent with `relative overflow-hidden`; cursor effects (FluidCursor,
181
+ SmoothCursor) mount once in the root +layout.svelte; Svelte 5 syntax only.
182
+ ```
183
+
151
184
  ## Development
152
185
 
153
186
  ```bash
@@ -181,7 +214,7 @@ src/
181
214
 
182
215
  ## Contributing
183
216
 
184
- Contributions are welcome! 52 components and counting — PRs for new components, bug fixes, and improvements are appreciated.
217
+ Contributions are welcome! 56 components and counting — PRs for new components, bug fixes, and improvements are appreciated.
185
218
 
186
219
  ### Adding a new component
187
220
 
@@ -1,4 +1,4 @@
1
1
  export { default as Dock } from "./Dock.svelte";
2
2
  export { default as DockIcon } from "./DockIcon.svelte";
3
3
  export { default as DockSeparator } from "./DockSeparator.svelte";
4
- export * from "./types";
4
+ export type { DataOrientation, Direction, DockContext } from "./types.js";
@@ -1,4 +1,3 @@
1
1
  export { default as Dock } from "./Dock.svelte";
2
2
  export { default as DockIcon } from "./DockIcon.svelte";
3
3
  export { default as DockSeparator } from "./DockSeparator.svelte";
4
- export * from "./types";
@@ -0,0 +1,181 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Props for EditorialEngine
4
+ *
5
+ * Live magazine layout: multi-column text flow with cursor handoff,
6
+ * auto-fitted headline, drop cap, pullquotes, and body text reflowing in
7
+ * real time around draggable animated orbs. All line breaking is computed
8
+ * by @chenglou/pretext — the render loop performs zero DOM reads.
9
+ *
10
+ * Inspired by the pretext "editorial engine" demo
11
+ * (https://chenglou.me/pretext/editorial-engine/).
12
+ */
13
+ export interface EditorialEngineProps {
14
+ /** Headline, auto-sized to the widest font that breaks no word */
15
+ headline?: string;
16
+ /** Body copy (plain text, paragraphs separated by blank lines) */
17
+ body?: string;
18
+ /** Up to two pullquotes embedded in the columns */
19
+ pullquotes?: string[];
20
+ /** Font family stack used for all text */
21
+ fontFamily?: string;
22
+ /** Additional CSS classes for the stage */
23
+ class?: string;
24
+ }
25
+ </script>
26
+
27
+ <script lang="ts">
28
+ import { cn } from "../../utils.js";
29
+ import { createEditorialEngine } from "./engine.js";
30
+
31
+ const DEFAULT_HEADLINE = "TEXT THAT FLOWS AROUND ANYTHING";
32
+
33
+ const DEFAULT_BODY = `Print designers have flowed text around images, illustrations, and odd-shaped figures for more than a century. The web never caught up. CSS gives us float, and more recently shape-outside, but both only push text away from one side of one static box. Flowing text through multiple columns, around several moving obstacles at once, filling the gaps on both sides — the browser has no answer for that.
34
+
35
+ The reason is not a lack of imagination. It is the cost of asking. Every question about text — how wide is this word, where does this line break, how tall is this paragraph — must go through the browser's layout engine, and the layout engine answers by reflowing the document. Ask too often and the interface stutters. So we learned not to ask, and our text stayed in rigid rectangles.
36
+
37
+ This page asks hundreds of times per frame. Every line of text you are reading was positioned by arithmetic, not by the browser. The pretext library measured every word once, up front, against a canvas font engine. After that, layout is pure math: given a width, it returns line breaks, line widths, and a cursor pointing at exactly where the text stopped.
38
+
39
+ That cursor is what makes columns work. The first column consumes text until it runs out of vertical space, then hands its cursor to the second column, which continues mid-sentence without duplication or gaps. Newspapers have done this on paper forever; on the web it has always required hidden elements and fragile content splitting.
40
+
41
+ The orbs drifting across the page are obstacles, not decoration. Each frame, the engine intersects every text line's vertical band with every orb, subtracts the blocked intervals from the column width, and fills every remaining slot — text flows on both sides of an orb at once, something shape-outside cannot express at all.
42
+
43
+ Drag an orb and the paragraphs part around it in real time. Click one and it pauses. Resize the window and the headline re-fits itself, searching for the largest font size that breaks no word across lines — a binary search that costs microseconds because each probe is arithmetic, not reflow.
44
+
45
+ The drop cap at the top of this article is another obstacle: a rectangle carved out of the first column's first three lines. The pullquotes are the same trick at a larger scale. Everything you see is one mechanism — carve the blocked intervals out of each line's band, lay out text in whatever space remains.
46
+
47
+ When the engine finishes a frame, it writes positions and text content to a pool of absolutely positioned elements, and nothing else. The browser never computes layout for this page's body text, because every coordinate is already known. A full reflow of every line around five moving obstacles typically costs a fraction of a millisecond.
48
+
49
+ Cheap text measurement changes what interfaces can be. Virtualized lists can know every item's exact height before rendering it. Chat bubbles can shrink-wrap to their tightest width. Labels can dodge chart elements as you zoom. Documents can flow around widgets that other people are dragging in real time.
50
+
51
+ None of this needs a new browser API or a standards process. It needs cached font metrics, a line breaker, and the willingness to stop asking the DOM questions it answers slowly. The text on this page stopped asking. And so it flows.`;
52
+
53
+ const DEFAULT_PULLQUOTES = [
54
+ "“Text flows on both sides of a moving obstacle at once — something shape-outside cannot express at all.”",
55
+ "“Every coordinate is already known. The browser never computes layout for this text.”",
56
+ ];
57
+
58
+ const DEFAULT_FONT_FAMILY =
59
+ '"Iowan Old Style", "Palatino Linotype", "Book Antiqua", Palatino, serif';
60
+
61
+ let {
62
+ headline = DEFAULT_HEADLINE,
63
+ body = DEFAULT_BODY,
64
+ pullquotes = DEFAULT_PULLQUOTES,
65
+ fontFamily = DEFAULT_FONT_FAMILY,
66
+ class: className,
67
+ }: EditorialEngineProps = $props();
68
+
69
+ let stageRef: HTMLDivElement;
70
+ let ready = $state(false);
71
+
72
+ // Re-created whenever the text props change, so the prepared measurements
73
+ // always match the rendered content. Effects never run during SSR.
74
+ $effect(() => {
75
+ const options = { headline, body, pullquotes, fontFamily };
76
+ let destroy: (() => void) | null = null;
77
+ let cancelled = false;
78
+
79
+ // pretext measures against the canvas font engine — fonts must be
80
+ // loaded first or widths come from the fallback font.
81
+ document.fonts.ready.then(() => {
82
+ if (cancelled) return;
83
+ destroy = createEditorialEngine(stageRef, options);
84
+ ready = true;
85
+ });
86
+
87
+ return () => {
88
+ cancelled = true;
89
+ destroy?.();
90
+ };
91
+ });
92
+ </script>
93
+
94
+ <div
95
+ bind:this={stageRef}
96
+ class={cn("ee-stage", className)}
97
+ style:font-family={fontFamily}
98
+ class:ready
99
+ >
100
+ {#if !ready}
101
+ <!-- SSR / pre-engine fallback: keeps the content in the HTML -->
102
+ <div class="ee-fallback" aria-hidden="false">
103
+ <h2>{headline}</h2>
104
+ <p>{body}</p>
105
+ </div>
106
+ {/if}
107
+ </div>
108
+
109
+ <style>
110
+ .ee-stage {
111
+ position: relative;
112
+ width: 100%;
113
+ height: 100%;
114
+ overflow: hidden;
115
+ background: radial-gradient(ellipse at 50% 40%, #0f0f14 0%, #0a0a0c 100%);
116
+ color: #e8e4dc;
117
+ touch-action: none;
118
+ }
119
+
120
+ .ee-fallback {
121
+ position: absolute;
122
+ inset: 0;
123
+ padding: 48px;
124
+ overflow: hidden;
125
+ opacity: 0.4;
126
+ font-size: 14px;
127
+ }
128
+
129
+ .ee-stage :global(.ee-line) {
130
+ position: absolute;
131
+ white-space: pre;
132
+ z-index: 1;
133
+ color: #e8e4dc;
134
+ user-select: text;
135
+ -webkit-user-select: text;
136
+ }
137
+
138
+ .ee-stage :global(.ee-headline-line) {
139
+ position: absolute;
140
+ white-space: pre;
141
+ font-weight: 700;
142
+ color: #fff;
143
+ letter-spacing: -0.5px;
144
+ z-index: 2;
145
+ user-select: text;
146
+ -webkit-user-select: text;
147
+ }
148
+
149
+ .ee-stage :global(.ee-drop-cap) {
150
+ position: absolute;
151
+ pointer-events: none;
152
+ z-index: 2;
153
+ font-weight: 700;
154
+ color: #c4a35a;
155
+ }
156
+
157
+ .ee-stage :global(.ee-orb) {
158
+ position: absolute;
159
+ border-radius: 50%;
160
+ pointer-events: none;
161
+ z-index: 10;
162
+ will-change: transform;
163
+ }
164
+
165
+ .ee-stage :global(.ee-pullquote-box) {
166
+ position: absolute;
167
+ pointer-events: none;
168
+ z-index: 3;
169
+ border-left: 3px solid #6b5a3d;
170
+ padding-left: 14px;
171
+ }
172
+
173
+ .ee-stage :global(.ee-pullquote-line) {
174
+ position: absolute;
175
+ white-space: pre;
176
+ font-style: italic;
177
+ color: #b8a070;
178
+ user-select: text;
179
+ -webkit-user-select: text;
180
+ }
181
+ </style>
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Props for EditorialEngine
3
+ *
4
+ * Live magazine layout: multi-column text flow with cursor handoff,
5
+ * auto-fitted headline, drop cap, pullquotes, and body text reflowing in
6
+ * real time around draggable animated orbs. All line breaking is computed
7
+ * by @chenglou/pretext — the render loop performs zero DOM reads.
8
+ *
9
+ * Inspired by the pretext "editorial engine" demo
10
+ * (https://chenglou.me/pretext/editorial-engine/).
11
+ */
12
+ export interface EditorialEngineProps {
13
+ /** Headline, auto-sized to the widest font that breaks no word */
14
+ headline?: string;
15
+ /** Body copy (plain text, paragraphs separated by blank lines) */
16
+ body?: string;
17
+ /** Up to two pullquotes embedded in the columns */
18
+ pullquotes?: string[];
19
+ /** Font family stack used for all text */
20
+ fontFamily?: string;
21
+ /** Additional CSS classes for the stage */
22
+ class?: string;
23
+ }
24
+ declare const EditorialEngine: import("svelte").Component<EditorialEngineProps, {}, "">;
25
+ type EditorialEngine = ReturnType<typeof EditorialEngine>;
26
+ export default EditorialEngine;
@@ -0,0 +1,56 @@
1
+ # EditorialEngine
2
+
3
+ Live magazine layout powered by [@chenglou/pretext](https://github.com/chenglou/pretext):
4
+ multi-column text flow with cursor handoff, auto-fitted headline, drop cap,
5
+ pullquotes, and body text reflowing in real time around draggable animated
6
+ orbs. The render loop performs **zero DOM reads** — every line position is
7
+ computed arithmetically from cached font metrics, and the only DOM operations
8
+ are writes (`left`, `top`, `textContent`).
9
+
10
+ Inspired by the pretext
11
+ [editorial engine demo](https://chenglou.me/pretext/editorial-engine/).
12
+
13
+ ## How it works
14
+
15
+ - `prepareWithSegments(body, font)` measures the text once against the canvas
16
+ font engine.
17
+ - Each frame, every text line's vertical band is intersected with each orb;
18
+ blocked horizontal intervals are subtracted from the column width and
19
+ `layoutNextLine` fills every remaining slot — text flows on **both sides**
20
+ of an obstacle, which CSS `shape-outside` cannot do.
21
+ - Columns hand a `LayoutCursor` to one another, so text continues
22
+ mid-sentence across column boundaries like print.
23
+ - The headline binary-searches the largest font size that breaks no word
24
+ (`walkLineRanges` to detect intra-word breaks).
25
+
26
+ ## Usage
27
+
28
+ ```svelte
29
+ <script lang="ts">
30
+ import { EditorialEngine } from "$lib/fancy-ui/editorial-engine";
31
+ </script>
32
+
33
+ <div class="h-[85vh]">
34
+ <EditorialEngine />
35
+ </div>
36
+ ```
37
+
38
+ The stage fills its parent — give the parent an explicit height.
39
+
40
+ ## Props
41
+
42
+ | Prop | Type | Default | Description |
43
+ | ------------ | ---------- | -------------------- | ------------------------------------------------- |
44
+ | `headline` | `string` | built-in copy | Auto-sized headline (largest size, no word break) |
45
+ | `body` | `string` | built-in copy | Body text, paragraphs separated by blank lines |
46
+ | `pullquotes` | `string[]` | built-in quotes | Up to two pullquotes embedded in the columns |
47
+ | `fontFamily` | `string` | Palatino serif stack | Font stack for all text |
48
+ | `class` | `string` | — | Additional CSS classes for the stage |
49
+
50
+ ## Interactions
51
+
52
+ - **Drag** an orb — text reparts around it every frame.
53
+ - **Click** an orb — pauses/resumes its drift.
54
+ - **Resize** — headline re-fits, column count adapts (1/2/3), text reflows.
55
+ - Text stays selectable; rendering pauses while a selection is active.
56
+ - `prefers-reduced-motion` starts all orbs paused (still draggable).
@@ -0,0 +1,7 @@
1
+ export interface EditorialEngineOptions {
2
+ headline: string;
3
+ body: string;
4
+ pullquotes: string[];
5
+ fontFamily: string;
6
+ }
7
+ export declare function createEditorialEngine(stage: HTMLDivElement, options: EditorialEngineOptions): () => void;