fancy-ui-svelte 0.5.0 → 0.7.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 (43) hide show
  1. package/README.md +98 -74
  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/frosted-glass/FrostedGlass.svelte +202 -0
  12. package/dist/fancy-ui/frosted-glass/FrostedGlass.svelte.d.ts +20 -0
  13. package/dist/fancy-ui/frosted-glass/FrostedGlass.test.d.ts +1 -0
  14. package/dist/fancy-ui/frosted-glass/FrostedGlass.test.js +48 -0
  15. package/dist/fancy-ui/frosted-glass/README.md +59 -0
  16. package/dist/fancy-ui/frosted-glass/index.d.ts +1 -0
  17. package/dist/fancy-ui/frosted-glass/index.js +1 -0
  18. package/dist/fancy-ui/index.d.ts +5 -0
  19. package/dist/fancy-ui/index.js +5 -0
  20. package/dist/fancy-ui/line-reveal/LineReveal.svelte +168 -0
  21. package/dist/fancy-ui/line-reveal/LineReveal.svelte.d.ts +33 -0
  22. package/dist/fancy-ui/line-reveal/README.md +42 -0
  23. package/dist/fancy-ui/line-reveal/index.d.ts +2 -0
  24. package/dist/fancy-ui/line-reveal/index.js +1 -0
  25. package/dist/fancy-ui/noise-reveal/NoiseReveal.svelte +323 -0
  26. package/dist/fancy-ui/noise-reveal/NoiseReveal.svelte.d.ts +19 -0
  27. package/dist/fancy-ui/noise-reveal/NoiseReveal.test.d.ts +1 -0
  28. package/dist/fancy-ui/noise-reveal/NoiseReveal.test.js +123 -0
  29. package/dist/fancy-ui/noise-reveal/index.d.ts +2 -0
  30. package/dist/fancy-ui/noise-reveal/index.js +1 -0
  31. package/dist/fancy-ui/registry.js +196 -11
  32. package/dist/fancy-ui/themes.d.ts +38 -0
  33. package/dist/fancy-ui/themes.js +356 -0
  34. package/dist/types.d.ts +2 -0
  35. package/dist/utils/animation.d.ts +109 -0
  36. package/dist/utils/animation.js +198 -0
  37. package/dist/utils/color.d.ts +128 -0
  38. package/dist/utils/color.js +314 -0
  39. package/dist/utils/geometry.d.ts +125 -0
  40. package/dist/utils/geometry.js +250 -0
  41. package/dist/utils/index.d.ts +8 -0
  42. package/dist/utils/index.js +39 -0
  43. package/package.json +13 -3
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
 
@@ -31,88 +31,88 @@ Beautiful animation and UI components for **Svelte 5**.
31
31
 
32
32
  ### Buttons
33
33
 
34
- | Component | Description | Demo |
35
- |-----------|-------------|------|
36
- | RainbowButton | Animated rainbow gradient border effect | [Demo](https://fancy-ui.rama.app/demo/rainbow-button) |
37
- | RippleButton | Ripple click effect | [Demo](https://fancy-ui.rama.app/demo/ripple-button) |
38
- | ShimmerButton | Rotating conic-gradient shimmer border | [Demo](https://fancy-ui.rama.app/demo/shimmer-button) |
39
- | GradientButton | Rotating conic-gradient rainbow border | [Demo](https://fancy-ui.rama.app/demo/gradient-button) |
34
+ | Component | Description | Demo |
35
+ | ---------------------- | ---------------------------------------- | --------------------------------------------------------------- |
36
+ | RainbowButton | Animated rainbow gradient border effect | [Demo](https://fancy-ui.rama.app/demo/rainbow-button) |
37
+ | RippleButton | Ripple click effect | [Demo](https://fancy-ui.rama.app/demo/ripple-button) |
38
+ | ShimmerButton | Rotating conic-gradient shimmer border | [Demo](https://fancy-ui.rama.app/demo/shimmer-button) |
39
+ | GradientButton | Rotating conic-gradient rainbow border | [Demo](https://fancy-ui.rama.app/demo/gradient-button) |
40
40
  | InteractiveHoverButton | Hover effect revealing alternate content | [Demo](https://fancy-ui.rama.app/demo/interactive-hover-button) |
41
41
 
42
42
  ### Cards
43
43
 
44
- | Component | Description | Demo |
45
- |-----------|-------------|------|
46
- | Card3D | 3D tilt card with mouse tracking | [Demo](https://fancy-ui.rama.app/demo/card-3d) |
47
- | CardSpotlight | Spotlight effect following mouse | [Demo](https://fancy-ui.rama.app/demo/card-spotlight) |
44
+ | Component | Description | Demo |
45
+ | ------------------- | -------------------------------------------- | ------------------------------------------------------------ |
46
+ | Card3D | 3D tilt card with mouse tracking | [Demo](https://fancy-ui.rama.app/demo/card-3d) |
47
+ | CardSpotlight | Spotlight effect following mouse | [Demo](https://fancy-ui.rama.app/demo/card-spotlight) |
48
48
  | DirectionAwareHover | Overlay slides in from mouse entry direction | [Demo](https://fancy-ui.rama.app/demo/direction-aware-hover) |
49
- | FlipCard | Two-sided card with flip animation | [Demo](https://fancy-ui.rama.app/demo/flip-card) |
50
- | GlareCard | Glare reflection effect on hover | [Demo](https://fancy-ui.rama.app/demo/glare-card) |
51
- | TextRevealCard | Text reveal on hover | [Demo](https://fancy-ui.rama.app/demo/text-reveal-card) |
49
+ | FlipCard | Two-sided card with flip animation | [Demo](https://fancy-ui.rama.app/demo/flip-card) |
50
+ | GlareCard | Glare reflection effect on hover | [Demo](https://fancy-ui.rama.app/demo/glare-card) |
51
+ | TextRevealCard | Text reveal on hover | [Demo](https://fancy-ui.rama.app/demo/text-reveal-card) |
52
52
 
53
53
  ### Text & Typography
54
54
 
55
- | Component | Description | Demo |
56
- |-----------|-------------|------|
57
- | BlurReveal | Scroll-triggered blur-to-clear reveal | [Demo](https://fancy-ui.rama.app/demo/blur-reveal) |
58
- | BoxReveal | Sliding colored box reveal animation | [Demo](https://fancy-ui.rama.app/demo/box-reveal) |
59
- | ColourfulText | Per-character color animation | [Demo](https://fancy-ui.rama.app/demo/colourful-text) |
60
- | ContainerTextFlip | Text flipping inside a container | [Demo](https://fancy-ui.rama.app/demo/container-text-flip) |
61
- | FlipWords | Cycling word animation with per-letter effects | [Demo](https://fancy-ui.rama.app/demo/flip-words) |
62
- | HyperText | Character scramble on hover | [Demo](https://fancy-ui.rama.app/demo/hyper-text) |
63
- | LetterPullup | Staggered letter pull-up animation | [Demo](https://fancy-ui.rama.app/demo/letter-pullup) |
64
- | LineShadowText | Text with animated line shadow | [Demo](https://fancy-ui.rama.app/demo/line-shadow-text) |
65
- | NumberTicker | Animated number counter with easing | [Demo](https://fancy-ui.rama.app/demo/number-ticker) |
66
- | SparklesText | Animated SVG sparkle stars overlay | [Demo](https://fancy-ui.rama.app/demo/sparkles-text) |
67
- | TextGenerateEffect | Word-by-word text generation effect | [Demo](https://fancy-ui.rama.app/demo/text-generate-effect) |
55
+ | Component | Description | Demo |
56
+ | ------------------ | ---------------------------------------------- | ----------------------------------------------------------- |
57
+ | BlurReveal | Scroll-triggered blur-to-clear reveal | [Demo](https://fancy-ui.rama.app/demo/blur-reveal) |
58
+ | BoxReveal | Sliding colored box reveal animation | [Demo](https://fancy-ui.rama.app/demo/box-reveal) |
59
+ | ColourfulText | Per-character color animation | [Demo](https://fancy-ui.rama.app/demo/colourful-text) |
60
+ | ContainerTextFlip | Text flipping inside a container | [Demo](https://fancy-ui.rama.app/demo/container-text-flip) |
61
+ | FlipWords | Cycling word animation with per-letter effects | [Demo](https://fancy-ui.rama.app/demo/flip-words) |
62
+ | HyperText | Character scramble on hover | [Demo](https://fancy-ui.rama.app/demo/hyper-text) |
63
+ | LetterPullup | Staggered letter pull-up animation | [Demo](https://fancy-ui.rama.app/demo/letter-pullup) |
64
+ | LineShadowText | Text with animated line shadow | [Demo](https://fancy-ui.rama.app/demo/line-shadow-text) |
65
+ | NumberTicker | Animated number counter with easing | [Demo](https://fancy-ui.rama.app/demo/number-ticker) |
66
+ | SparklesText | Animated SVG sparkle stars overlay | [Demo](https://fancy-ui.rama.app/demo/sparkles-text) |
67
+ | TextGenerateEffect | Word-by-word text generation effect | [Demo](https://fancy-ui.rama.app/demo/text-generate-effect) |
68
68
 
69
69
  ### Backgrounds
70
70
 
71
- | Component | Description | Demo |
72
- |-----------|-------------|------|
73
- | FallingStarsBg | Canvas 3D starfield with motion trails | [Demo](https://fancy-ui.rama.app/demo/bg-falling-stars) |
74
- | FlickeringGrid | Canvas grid with flickering opacity | [Demo](https://fancy-ui.rama.app/demo/flickering-grid) |
75
- | InteractiveGridPattern | SVG grid with hover highlights | [Demo](https://fancy-ui.rama.app/demo/interactive-grid-pattern) |
76
- | StarsBackground | Starfield with parallax mouse tracking | [Demo](https://fancy-ui.rama.app/demo/bg-stars) |
71
+ | Component | Description | Demo |
72
+ | ---------------------- | -------------------------------------- | --------------------------------------------------------------- |
73
+ | FallingStarsBg | Canvas 3D starfield with motion trails | [Demo](https://fancy-ui.rama.app/demo/bg-falling-stars) |
74
+ | FlickeringGrid | Canvas grid with flickering opacity | [Demo](https://fancy-ui.rama.app/demo/flickering-grid) |
75
+ | InteractiveGridPattern | SVG grid with hover highlights | [Demo](https://fancy-ui.rama.app/demo/interactive-grid-pattern) |
76
+ | StarsBackground | Starfield with parallax mouse tracking | [Demo](https://fancy-ui.rama.app/demo/bg-stars) |
77
77
 
78
78
  ### Effects & Animations
79
79
 
80
- | Component | Description | Demo |
81
- |-----------|-------------|------|
82
- | AnimatedBeam | SVG beams connecting elements | [Demo](https://fancy-ui.rama.app/demo/animated-beam) |
83
- | BorderBeam | Beam effect traveling around borders | [Demo](https://fancy-ui.rama.app/demo/border-beam) |
84
- | Confetti | Configurable confetti burst | [Demo](https://fancy-ui.rama.app/demo/confetti) |
85
- | FluidCursor | WebGL fluid simulation following cursor | [Demo](https://fancy-ui.rama.app/demo/fluid-cursor) |
86
- | GlowBorder | Animated glowing border with gradients | [Demo](https://fancy-ui.rama.app/demo/glow-border) |
87
- | GlowingEffect | Glowing highlight on hover | [Demo](https://fancy-ui.rama.app/demo/glowing-effect) |
80
+ | Component | Description | Demo |
81
+ | ---------------- | ----------------------------------------- | --------------------------------------------------------- |
82
+ | AnimatedBeam | SVG beams connecting elements | [Demo](https://fancy-ui.rama.app/demo/animated-beam) |
83
+ | BorderBeam | Beam effect traveling around borders | [Demo](https://fancy-ui.rama.app/demo/border-beam) |
84
+ | Confetti | Configurable confetti burst | [Demo](https://fancy-ui.rama.app/demo/confetti) |
85
+ | FluidCursor | WebGL fluid simulation following cursor | [Demo](https://fancy-ui.rama.app/demo/fluid-cursor) |
86
+ | GlowBorder | Animated glowing border with gradients | [Demo](https://fancy-ui.rama.app/demo/glow-border) |
87
+ | GlowingEffect | Glowing highlight on hover | [Demo](https://fancy-ui.rama.app/demo/glowing-effect) |
88
88
  | ImageTrailCursor | Cursor-following image trail (8 variants) | [Demo](https://fancy-ui.rama.app/demo/image-trail-cursor) |
89
- | LiquidGlass | Liquid glass morphism effect | [Demo](https://fancy-ui.rama.app/demo/liquid-glass) |
90
- | Meteors | Animated meteor shower effect | [Demo](https://fancy-ui.rama.app/demo/meteors) |
91
- | NeonBorder | Dual-color neon glow border | [Demo](https://fancy-ui.rama.app/demo/neon-border) |
92
- | Ripple | Expanding ripple rings | [Demo](https://fancy-ui.rama.app/demo/ripple) |
93
- | SmoothCursor | Smooth lagging cursor follower | [Demo](https://fancy-ui.rama.app/demo/smooth-cursor) |
94
- | Sparkles | Particle sparkle canvas | [Demo](https://fancy-ui.rama.app/demo/sparkles) |
95
- | TracingBeam | Scroll-driven tracing beam | [Demo](https://fancy-ui.rama.app/demo/tracing-beam) |
89
+ | LiquidGlass | Liquid glass morphism effect | [Demo](https://fancy-ui.rama.app/demo/liquid-glass) |
90
+ | Meteors | Animated meteor shower effect | [Demo](https://fancy-ui.rama.app/demo/meteors) |
91
+ | NeonBorder | Dual-color neon glow border | [Demo](https://fancy-ui.rama.app/demo/neon-border) |
92
+ | Ripple | Expanding ripple rings | [Demo](https://fancy-ui.rama.app/demo/ripple) |
93
+ | SmoothCursor | Smooth lagging cursor follower | [Demo](https://fancy-ui.rama.app/demo/smooth-cursor) |
94
+ | Sparkles | Particle sparkle canvas | [Demo](https://fancy-ui.rama.app/demo/sparkles) |
95
+ | TracingBeam | Scroll-driven tracing beam | [Demo](https://fancy-ui.rama.app/demo/tracing-beam) |
96
96
 
97
97
  ### Layout
98
98
 
99
- | Component | Description | Demo |
100
- |-----------|-------------|------|
101
- | BentoGrid | Bento-style responsive grid layout | [Demo](https://fancy-ui.rama.app/demo/bento-grid) |
102
- | Book | 3D book flip animation | [Demo](https://fancy-ui.rama.app/demo/book) |
103
- | ContainerScroll | 3D scroll perspective container | [Demo](https://fancy-ui.rama.app/demo/container-scroll) |
104
- | Focus | Focus-expand card layout | [Demo](https://fancy-ui.rama.app/demo/focus) |
105
- | Marquee | Infinite scrolling for text, images, or cards | [Demo](https://fancy-ui.rama.app/demo/marquee) |
99
+ | Component | Description | Demo |
100
+ | --------------- | --------------------------------------------- | ------------------------------------------------------- |
101
+ | BentoGrid | Bento-style responsive grid layout | [Demo](https://fancy-ui.rama.app/demo/bento-grid) |
102
+ | Book | 3D book flip animation | [Demo](https://fancy-ui.rama.app/demo/book) |
103
+ | ContainerScroll | 3D scroll perspective container | [Demo](https://fancy-ui.rama.app/demo/container-scroll) |
104
+ | Focus | Focus-expand card layout | [Demo](https://fancy-ui.rama.app/demo/focus) |
105
+ | Marquee | Infinite scrolling for text, images, or cards | [Demo](https://fancy-ui.rama.app/demo/marquee) |
106
106
 
107
107
  ### Navigation & Display
108
108
 
109
- | Component | Description | Demo |
110
- |-----------|-------------|------|
111
- | AnimatedTooltip | Avatar row with mouse-following tooltips | [Demo](https://fancy-ui.rama.app/demo/animated-tooltip) |
112
- | Compare | Before/after image comparison slider | [Demo](https://fancy-ui.rama.app/demo/compare) |
113
- | Dock | macOS-style dock with icon magnification | [Demo](https://fancy-ui.rama.app/demo/dock) |
114
- | LogoCloud | Marquee, grid, and icon logo layouts | [Demo](https://fancy-ui.rama.app/demo/logo-cloud) |
115
- | Timeline | Vertical timeline with scroll-driven progress | [Demo](https://fancy-ui.rama.app/demo/timeline) |
109
+ | Component | Description | Demo |
110
+ | --------------- | --------------------------------------------- | ------------------------------------------------------- |
111
+ | AnimatedTooltip | Avatar row with mouse-following tooltips | [Demo](https://fancy-ui.rama.app/demo/animated-tooltip) |
112
+ | Compare | Before/after image comparison slider | [Demo](https://fancy-ui.rama.app/demo/compare) |
113
+ | Dock | macOS-style dock with icon magnification | [Demo](https://fancy-ui.rama.app/demo/dock) |
114
+ | LogoCloud | Marquee, grid, and icon logo layouts | [Demo](https://fancy-ui.rama.app/demo/logo-cloud) |
115
+ | Timeline | Vertical timeline with scroll-driven progress | [Demo](https://fancy-ui.rama.app/demo/timeline) |
116
116
 
117
117
  ## Quick Start
118
118
 
@@ -125,7 +125,7 @@ npm install fancy-ui-svelte
125
125
  Then import any component:
126
126
 
127
127
  ```ts
128
- import { Marquee, BorderBeam, Confetti } from 'fancy-ui-svelte';
128
+ import { Marquee, BorderBeam, Confetti } from "fancy-ui-svelte";
129
129
  ```
130
130
 
131
131
  Add the Tailwind integration to your app's CSS (e.g. `src/app.css`):
@@ -146,9 +146,11 @@ This single import tells Tailwind v4 to scan the library's components so all uti
146
146
  ```
147
147
 
148
148
  The path is relative to your CSS file location.
149
+
149
150
  </details>
150
151
 
151
152
  **Or browse and copy a component:**
153
+
152
154
  1. Find the component you need in the [live demo](https://fancy-ui.rama.app)
153
155
  2. Copy the source from `src/lib/fancy-ui/[component-name]/`
154
156
  3. Paste into your project
@@ -162,6 +164,25 @@ pnpm install
162
164
  pnpm dev
163
165
  ```
164
166
 
167
+ ## Using with AI agents (Claude Code, Cursor, Copilot)
168
+
169
+ The docs site serves LLM-friendly documentation following the [llms.txt](https://llmstxt.org) convention:
170
+
171
+ - [fancy-ui.rama.app/llms.txt](https://fancy-ui.rama.app/llms.txt) — setup guide, usage rules, and component index
172
+ - [fancy-ui.rama.app/llms-full.txt](https://fancy-ui.rama.app/llms-full.txt) — full reference with every component's props
173
+
174
+ To make your coding agent use fancy-ui correctly, add this to your project's `CLAUDE.md`, `AGENTS.md`, or `.cursorrules`:
175
+
176
+ ```markdown
177
+ ## UI components
178
+
179
+ Use fancy-ui-svelte (Svelte 5 + Tailwind CSS v4) for animated UI components.
180
+ Full component and props reference: https://fancy-ui.rama.app/llms-full.txt
181
+ Key rules: overlay/effect components (BorderBeam, GlowBorder, backgrounds, ...)
182
+ need a parent with `relative overflow-hidden`; cursor effects (FluidCursor,
183
+ SmoothCursor) mount once in the root +layout.svelte; Svelte 5 syntax only.
184
+ ```
185
+
165
186
  ## Development
166
187
 
167
188
  ```bash
@@ -170,8 +191,11 @@ pnpm check # Run Svelte type checker
170
191
  pnpm test # Run tests
171
192
  pnpm test:watch # Run tests in watch mode
172
193
  pnpm build # Production build
194
+ pnpm storybook # Component workshop on :6006
173
195
  ```
174
196
 
197
+ Component stories live in `src/stories/` — see the [Storybook section in CONTRIBUTING.md](./CONTRIBUTING.md#storybook).
198
+
175
199
  ## Project Structure
176
200
 
177
201
  ```
@@ -195,7 +219,7 @@ src/
195
219
 
196
220
  ## Contributing
197
221
 
198
- Contributions are welcome! 52 components and counting — PRs for new components, bug fixes, and improvements are appreciated.
222
+ Contributions are welcome! 56 components and counting — PRs for new components, bug fixes, and improvements are appreciated.
199
223
 
200
224
  ### Adding a new component
201
225
 
@@ -207,15 +231,15 @@ Contributions are welcome! 52 components and counting — PRs for new components
207
231
 
208
232
  ## Tech Stack
209
233
 
210
- | Technology | Version | Purpose |
211
- |------------|---------|---------|
212
- | [Svelte](https://svelte.dev) | 5 | UI framework |
213
- | [SvelteKit](https://svelte.dev/docs/kit) | 2 | App framework |
214
- | [Tailwind CSS](https://tailwindcss.com) | 4 | Styling |
215
- | [TypeScript](https://www.typescriptlang.org) | 5 | Type safety |
216
- | [Vitest](https://vitest.dev) | 4 | Testing |
217
- | [bits-ui](https://bits-ui.com) | 2 | Headless primitives |
218
- | [GSAP](https://gsap.com) | 3 | Advanced animations |
234
+ | Technology | Version | Purpose |
235
+ | -------------------------------------------- | ------- | ------------------- |
236
+ | [Svelte](https://svelte.dev) | 5 | UI framework |
237
+ | [SvelteKit](https://svelte.dev/docs/kit) | 2 | App framework |
238
+ | [Tailwind CSS](https://tailwindcss.com) | 4 | Styling |
239
+ | [TypeScript](https://www.typescriptlang.org) | 5 | Type safety |
240
+ | [Vitest](https://vitest.dev) | 4 | Testing |
241
+ | [bits-ui](https://bits-ui.com) | 2 | Headless primitives |
242
+ | [GSAP](https://gsap.com) | 3 | Advanced animations |
219
243
 
220
244
  ## Credits
221
245
 
@@ -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;