fancy-ui-react 0.3.0 → 0.4.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 (86) hide show
  1. package/README.md +52 -19
  2. package/dist/components/animated-beam/AnimatedBeam.d.ts +47 -11
  3. package/dist/components/animated-beam/AnimatedBeam.js +293 -118
  4. package/dist/components/animated-tooltip/AnimatedTooltip.d.ts +10 -1
  5. package/dist/components/animated-tooltip/AnimatedTooltip.js +170 -101
  6. package/dist/components/bento-grid/BentoFrame.d.ts +16 -0
  7. package/dist/components/bento-grid/BentoFrame.js +28 -0
  8. package/dist/components/bento-grid/BentoGrid.d.ts +5 -1
  9. package/dist/components/bento-grid/BentoGrid.js +71 -2
  10. package/dist/components/bento-grid/BentoGridCard.js +34 -26
  11. package/dist/components/bento-grid/BentoGridItem.js +10 -10
  12. package/dist/components/border-beam/BorderBeam.d.ts +55 -10
  13. package/dist/components/border-beam/BorderBeam.js +93 -16
  14. package/dist/components/compare/Compare.d.ts +32 -8
  15. package/dist/components/compare/Compare.js +213 -95
  16. package/dist/components/container-scroll/ContainerScroll.d.ts +26 -4
  17. package/dist/components/container-scroll/ContainerScroll.js +88 -49
  18. package/dist/components/dock/Dock.d.ts +10 -7
  19. package/dist/components/dock/Dock.js +76 -31
  20. package/dist/components/dock/DockIcon.d.ts +14 -3
  21. package/dist/components/dock/DockIcon.js +23 -19
  22. package/dist/components/dock/DockSeparator.d.ts +4 -1
  23. package/dist/components/dock/DockSeparator.js +5 -2
  24. package/dist/components/dock/types.d.ts +13 -6
  25. package/dist/components/dropdown-menu/menu-shared.d.ts +11 -8
  26. package/dist/components/editorial-engine/engine.js +1 -14
  27. package/dist/components/fireworks-hdr/webgl2-renderer.js +0 -19
  28. package/dist/components/fireworks-hdr/webgpu-renderer.js +3 -22
  29. package/dist/components/flip-card/FlipCard.d.ts +42 -1
  30. package/dist/components/flip-card/FlipCard.js +178 -25
  31. package/dist/components/flip-card/index.d.ts +1 -1
  32. package/dist/components/fluid-cursor/FluidCursor.js +5 -4
  33. package/dist/components/fluid-cursor/dev.d.ts +1 -0
  34. package/dist/components/fluid-cursor/dev.js +8 -0
  35. package/dist/components/fluid-cursor/fluid-shared.d.ts +0 -2
  36. package/dist/components/fluid-cursor/fluid-shared.js +0 -2
  37. package/dist/components/fluid-cursor/webgpu-engine.js +10 -24
  38. package/dist/components/glow-border/GlowBorder.d.ts +41 -1
  39. package/dist/components/glow-border/GlowBorder.js +96 -16
  40. package/dist/components/glow-border/index.d.ts +2 -2
  41. package/dist/components/gradient-button/GradientButton.d.ts +10 -5
  42. package/dist/components/gradient-button/GradientButton.js +25 -18
  43. package/dist/components/image-trail-cursor/ImageTrailCursor.js +1 -0
  44. package/dist/components/image-trail-cursor/trail-variants.d.ts +37 -1
  45. package/dist/components/image-trail-cursor/trail-variants.js +331 -8
  46. package/dist/components/interactive-hover-button/InteractiveHoverButton.js +30 -35
  47. package/dist/components/marquee/Marquee.d.ts +7 -3
  48. package/dist/components/marquee/Marquee.js +134 -2
  49. package/dist/components/marquee/ReviewCard.d.ts +2 -1
  50. package/dist/components/marquee/ReviewCard.js +20 -9
  51. package/dist/components/meteors/Meteors.d.ts +32 -10
  52. package/dist/components/meteors/Meteors.js +53 -20
  53. package/dist/components/mosaic-glow/mosaic-glow-core.d.ts +98 -10
  54. package/dist/components/neon-border/NeonBorder.d.ts +24 -5
  55. package/dist/components/neon-border/NeonBorder.js +37 -7
  56. package/dist/components/pagination/Pagination.js +220 -82
  57. package/dist/components/rainbow-button/RainbowButton.js +13 -5
  58. package/dist/components/ripple-button/RippleButton.d.ts +20 -1
  59. package/dist/components/ripple-button/RippleButton.js +44 -21
  60. package/dist/components/shimmer-button/ShimmerButton.d.ts +5 -5
  61. package/dist/components/shimmer-button/ShimmerButton.js +17 -19
  62. package/dist/components/smooth-cursor/SmoothCursor.d.ts +13 -9
  63. package/dist/components/smooth-cursor/SmoothCursor.js +114 -163
  64. package/dist/components/smooth-cursor/smooth-cursor-core.d.ts +85 -0
  65. package/dist/components/smooth-cursor/smooth-cursor-core.js +327 -0
  66. package/dist/components/stepper/Step.js +32 -19
  67. package/dist/components/stepper/Stepper.js +18 -2
  68. package/dist/components/stepper/types.d.ts +13 -0
  69. package/dist/components/timeline/Timeline.d.ts +8 -1
  70. package/dist/components/timeline/Timeline.js +165 -76
  71. package/dist/index.js +2 -1
  72. package/dist/internals/markdown.d.ts +5 -0
  73. package/dist/internals/motion/haptics.d.ts +0 -4
  74. package/dist/internals/motion/presence-core.d.ts +59 -0
  75. package/dist/internals/motion/presence-core.js +162 -0
  76. package/dist/internals/motion/presence.d.ts +2 -4
  77. package/dist/internals/motion/presence.js +1 -156
  78. package/dist/internals/motion/raf.d.ts +1 -1
  79. package/dist/internals/motion/stagger.d.ts +3 -4
  80. package/dist/internals/motion/tokens.js +4 -0
  81. package/dist/internals/motion/transitions.d.ts +17 -17
  82. package/dist/internals/motion/types.d.ts +6 -7
  83. package/dist/internals/relative-time.d.ts +1 -1
  84. package/dist/styles.css +3470 -203
  85. package/package.json +18 -2
  86. package/dist/components/fireworks-hdr/webgpu-types.d.ts +0 -159
@@ -1,133 +1,202 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { useState, useCallback, useRef } from "react";
3
+ import { useState, useRef, useCallback, useEffect } from "react";
4
4
  import { cn } from "../../utils.js";
5
- import { useComposedRefs } from "../../internals/dom/use-composed-refs.js";
5
+ import { useLiveRef } from "../../internals/dom/use-live-ref.js";
6
+ import { linear } from "../../internals/motion/easing.js";
7
+ import { useReducedMotion } from "../../internals/motion/media-query.js";
6
8
  import { usePresence } from "../../internals/motion/presence.js";
7
- function cubicOut(t) {
8
- const f = t - 1;
9
- return f * f * f + 1;
9
+ /* empty css */
10
+ function tooltipId(itemId) {
11
+ return `animated-tooltip-${itemId}`;
12
+ }
13
+ function transformFor(mouseX, size, reduced) {
14
+ const lean = reduced ? 0 : Math.max(-1, Math.min(1, mouseX / (size / 2)));
15
+ const rotation = lean * 7;
16
+ const translation = lean * 14;
17
+ return `translateX(calc(-50% + ${translation}px)) rotate(${rotation}deg)`;
10
18
  }
11
- function tooltipScale(node) {
12
- const style = getComputedStyle(node);
13
- const targetOpacity = +style.opacity;
14
- const transform = style.transform === "none" ? "" : style.transform;
15
- const sd = 1 - 0.6;
16
- const od = targetOpacity;
19
+ function partOffset(i, activeIndex, reduced) {
20
+ if (activeIndex < 0 || reduced) return 0;
21
+ const d = i - activeIndex;
22
+ const distance = Math.abs(d);
23
+ if (distance === 1) return Math.sign(d) * 6;
24
+ if (distance === 2) return Math.sign(d) * 2;
25
+ return 0;
26
+ }
27
+ function sink(_node, params) {
28
+ if (!params || params.entering) {
29
+ return { delay: 0, duration: 0, easing: linear, css: () => "" };
30
+ }
31
+ const still = params.still;
17
32
  return {
18
33
  delay: 0,
19
- duration: 200,
20
- easing: cubicOut,
21
- css: (t, u) => `
22
- transform: ${transform} scale(${1 - sd * u});
23
- opacity: ${targetOpacity - od * u}
24
- `
34
+ duration: still ? 120 : 160,
35
+ easing: (t) => t * t,
36
+ css: (t) => still ? `opacity: ${t};` : `opacity: ${t}; transform: translateY(${(1 - t) * 4}px) scale(${0.97 + 0.03 * t});`
25
37
  };
26
38
  }
27
- function poseAt(event) {
28
- const rect = event.currentTarget.getBoundingClientRect();
29
- const halfWidth = rect.width / 2;
30
- const mouseX = event.clientX - rect.left - halfWidth;
31
- const value = mouseX / 100 * 50;
32
- return { rotation: value, translation: value };
33
- }
34
- function transformFor(pose) {
35
- return `translateX(calc(-50% + ${pose.translation}px)) rotate(${pose.rotation}deg)`;
36
- }
37
- function tooltipId(itemId) {
38
- return `animated-tooltip-${itemId}`;
39
- }
40
- function AvatarItem({ item, hovered, onHoverStart, onHoverEnd }) {
41
- const pose = useRef({ rotation: 0, translation: 0 });
42
- const tooltip = useRef(null);
43
- const spec = useRef(null);
44
- const presence = usePresence(hovered, {
45
- onEnterEnd: () => {
46
- spec.current = null;
47
- }
48
- });
49
- const attach = useCallback((node) => {
50
- tooltip.current = node;
51
- if (node) {
52
- node.style.transform = transformFor(pose.current);
53
- return;
54
- }
55
- spec.current = null;
56
- }, []);
57
- const tooltipRef = useComposedRefs(
58
- attach,
59
- presence.register((node) => spec.current ??= tooltipScale(node))
39
+ function AvatarItem({
40
+ item,
41
+ active,
42
+ shift,
43
+ reduced,
44
+ onEnter,
45
+ onMove,
46
+ onLeave,
47
+ onFocusIn,
48
+ onTip
49
+ }) {
50
+ const presence = usePresence(active);
51
+ const cardRef = presence.register(sink, (entering) => ({ entering, still: reduced }));
52
+ const tipNode = useRef(null);
53
+ const itemId = item.id;
54
+ const attachTip = useCallback(
55
+ (node) => {
56
+ if (node) {
57
+ tipNode.current = node;
58
+ onTip(itemId, node, null);
59
+ return;
60
+ }
61
+ onTip(itemId, null, tipNode.current);
62
+ tipNode.current = null;
63
+ },
64
+ [itemId, onTip]
60
65
  );
61
- function draw(next) {
62
- pose.current = next;
63
- if (tooltip.current) tooltip.current.style.transform = transformFor(next);
64
- }
65
- function handleMouseEnter(event) {
66
- draw(poseAt(event));
67
- onHoverStart(item.id);
68
- }
69
- function handleMouseMove(event) {
70
- if (!hovered) return;
71
- draw(poseAt(event));
72
- }
73
- function handleFocus() {
74
- draw({ rotation: 0, translation: 0 });
75
- onHoverStart(item.id);
76
- }
77
66
  return /* @__PURE__ */ jsxs(
78
67
  "div",
79
68
  {
80
- className: "group relative -mr-4",
81
- onMouseEnter: handleMouseEnter,
82
- onMouseLeave: onHoverEnd,
83
- onMouseMove: handleMouseMove,
84
- onFocus: handleFocus,
85
- onBlur: onHoverEnd,
69
+ className: cn("at-item group relative", active && "at-active", shift !== 0 && "at-parted"),
70
+ style: { "--_at-shift": `${shift}px` },
71
+ "data-active": active ? "" : void 0,
72
+ "data-part": shift < 0 ? "before" : shift > 0 ? "after" : void 0,
73
+ onMouseEnter: (event) => onEnter(item.id, event),
74
+ onMouseLeave: onLeave,
75
+ onMouseMove: onMove,
76
+ onFocus: () => onFocusIn(item.id),
77
+ onBlur: onLeave,
86
78
  tabIndex: 0,
87
- "aria-describedby": hovered ? tooltipId(item.id) : void 0,
79
+ "aria-describedby": active ? tooltipId(item.id) : void 0,
88
80
  children: [
89
- presence.mounted && /* @__PURE__ */ jsxs(
81
+ presence.mounted && /* @__PURE__ */ jsx(
90
82
  "div",
91
83
  {
92
- ref: tooltipRef,
84
+ ref: attachTip,
93
85
  id: tooltipId(item.id),
94
86
  role: "tooltip",
95
- className: "pointer-events-none absolute -top-16 left-1/2 z-50 flex flex-col items-center justify-center rounded-md bg-black px-4 py-2 text-xs whitespace-nowrap shadow-xl",
96
- children: [
97
- /* @__PURE__ */ jsx("div", { className: "absolute right-1/2 -bottom-px z-30 me-1 h-px w-2/5 translate-x-1/2 bg-gradient-to-r from-transparent via-emerald-500 to-transparent" }),
98
- /* @__PURE__ */ jsx("div", { className: "absolute -bottom-px left-1/2 z-30 ms-1 h-px w-2/5 -translate-x-1/2 bg-gradient-to-r from-transparent via-sky-500 to-transparent" }),
99
- /* @__PURE__ */ jsx("div", { className: "relative z-30 text-base font-bold text-white", children: item.name }),
100
- /* @__PURE__ */ jsx("div", { className: "text-xs text-white", children: item.designation })
101
- ]
87
+ className: "at-tip pointer-events-none absolute left-1/2 z-50",
88
+ children: /* @__PURE__ */ jsxs("div", { ref: cardRef, className: "at-card", children: [
89
+ /* @__PURE__ */ jsx("span", { className: "at-pointer", "aria-hidden": "true" }),
90
+ /* @__PURE__ */ jsx("div", { className: "at-name", children: item.name }),
91
+ /* @__PURE__ */ jsx("span", { className: "at-rule", "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", { className: "at-glint" }) }),
92
+ /* @__PURE__ */ jsx("div", { className: "at-role", children: item.designation })
93
+ ] })
102
94
  }
103
95
  ),
104
- /* @__PURE__ */ jsx(
105
- "img",
106
- {
107
- src: item.image,
108
- alt: item.name,
109
- className: "relative !m-0 size-14 rounded-full border-2 border-white object-cover object-top !p-0 transition duration-500 group-hover:z-30 group-hover:scale-105"
110
- }
111
- )
96
+ /* @__PURE__ */ jsxs("div", { className: cn("at-avatar", active && !reduced && "at-lifted"), children: [
97
+ /* @__PURE__ */ jsx("span", { className: "at-glow", "aria-hidden": "true" }),
98
+ /* @__PURE__ */ jsx("span", { className: "at-disc", "aria-hidden": "true" }),
99
+ /* @__PURE__ */ jsx("span", { className: "at-ring", "aria-hidden": "true" }),
100
+ /* @__PURE__ */ jsx(
101
+ "img",
102
+ {
103
+ src: item.image,
104
+ alt: item.name,
105
+ className: "at-img relative !m-0 rounded-full object-cover object-top !p-0"
106
+ }
107
+ )
108
+ ] })
112
109
  ]
113
110
  }
114
111
  );
115
112
  }
116
- function AnimatedTooltip({ items, className }) {
117
- const [hoveredIndex, setHoveredIndex] = useState(null);
118
- const handleHoverStart = useCallback((itemId) => {
119
- setHoveredIndex(itemId);
120
- }, []);
121
- const handleHoverEnd = useCallback(() => {
122
- setHoveredIndex(null);
113
+ function AnimatedTooltip({ items, className, accent, size = 56 }) {
114
+ const reduced = useReducedMotion();
115
+ const [hoveredId, setHoveredId] = useState(null);
116
+ const mouseX = useRef(0);
117
+ const hovered = useRef(null);
118
+ const tips = useRef(/* @__PURE__ */ new Map());
119
+ const reducedRef = useLiveRef(reduced);
120
+ const sizeRef = useLiveRef(size);
121
+ const draw = useCallback(
122
+ (itemId) => {
123
+ const node = tips.current.get(itemId);
124
+ if (node)
125
+ node.style.transform = transformFor(mouseX.current, sizeRef.current, reducedRef.current);
126
+ },
127
+ [reducedRef, sizeRef]
128
+ );
129
+ const onTip = useCallback(
130
+ (itemId, node, previous) => {
131
+ if (node) {
132
+ tips.current.set(itemId, node);
133
+ draw(itemId);
134
+ return;
135
+ }
136
+ if (tips.current.get(itemId) === previous) tips.current.delete(itemId);
137
+ },
138
+ [draw]
139
+ );
140
+ const activate = useCallback(
141
+ (itemId) => {
142
+ hovered.current = itemId;
143
+ setHoveredId(itemId);
144
+ draw(itemId);
145
+ },
146
+ [draw]
147
+ );
148
+ const handleMouseEnter = useCallback(
149
+ (itemId, event) => {
150
+ const rect = event.currentTarget.getBoundingClientRect();
151
+ mouseX.current = event.clientX - rect.left - rect.width / 2;
152
+ activate(itemId);
153
+ },
154
+ [activate]
155
+ );
156
+ const handleMouseMove = useCallback(
157
+ (event) => {
158
+ if (hovered.current === null) return;
159
+ const rect = event.currentTarget.getBoundingClientRect();
160
+ mouseX.current = event.clientX - rect.left - rect.width / 2;
161
+ draw(hovered.current);
162
+ },
163
+ [draw]
164
+ );
165
+ const handleLeave = useCallback(() => {
166
+ hovered.current = null;
167
+ mouseX.current = 0;
168
+ setHoveredId(null);
123
169
  }, []);
124
- return /* @__PURE__ */ jsx("div", { className: cn("flex flex-row items-center", className), children: items.map((item) => /* @__PURE__ */ jsx(
170
+ const handleFocusIn = useCallback(
171
+ (itemId) => {
172
+ mouseX.current = 0;
173
+ activate(itemId);
174
+ },
175
+ [activate]
176
+ );
177
+ useEffect(() => {
178
+ if (hovered.current !== null) draw(hovered.current);
179
+ }, [draw, reduced, size]);
180
+ const activeIndex = hoveredId === null ? -1 : items.findIndex((item) => item.id === hoveredId);
181
+ const rootStyle = {
182
+ "--_at-size": `${size}px`,
183
+ ...accent ? {
184
+ "--at-accent": accent,
185
+ "--at-accent-2": `color-mix(in oklab, ${accent} 62%, white)`
186
+ } : {}
187
+ };
188
+ return /* @__PURE__ */ jsx("div", { className: cn("at-root flex flex-row items-center", className), style: rootStyle, children: items.map((item, i) => /* @__PURE__ */ jsx(
125
189
  AvatarItem,
126
190
  {
127
191
  item,
128
- hovered: hoveredIndex === item.id,
129
- onHoverStart: handleHoverStart,
130
- onHoverEnd: handleHoverEnd
192
+ active: hoveredId === item.id,
193
+ shift: partOffset(i, activeIndex, reduced),
194
+ reduced,
195
+ onEnter: handleMouseEnter,
196
+ onMove: handleMouseMove,
197
+ onLeave: handleLeave,
198
+ onFocusIn: handleFocusIn,
199
+ onTip
131
200
  },
132
201
  item.id
133
202
  )) });
@@ -0,0 +1,16 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ /**
3
+ * Internal: the nested double frame both BentoGridItem and BentoGridCard are
4
+ * drawn in. Not exported from index.ts — the two public tiles own their
5
+ * content, this owns the chrome (outer hairline frame, inner lit panel, the
6
+ * accent glow that rises from the bottom on hover, and the bottom-edge light
7
+ * line).
8
+ */
9
+ export interface BentoFrameProps extends HTMLAttributes<HTMLDivElement> {
10
+ /** Classes for the outer frame (the grid item itself). */
11
+ className?: string;
12
+ /** Classes for the inner panel. */
13
+ panelClass?: string;
14
+ children?: ReactNode;
15
+ }
16
+ export declare function BentoFrame({ className, panelClass, children, ...rest }: BentoFrameProps): import("react").JSX.Element;
@@ -0,0 +1,28 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { cn } from "../../utils.js";
4
+ /* empty css */
5
+ function BentoFrame({
6
+ className = "",
7
+ panelClass = "",
8
+ children,
9
+ ...rest
10
+ }) {
11
+ return /* @__PURE__ */ jsx("div", { className: cn("bento-tile relative flex rounded-2xl p-1.5", className), ...rest, children: /* @__PURE__ */ jsxs(
12
+ "div",
13
+ {
14
+ className: cn(
15
+ "bento-panel relative isolate flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden rounded-[10px]",
16
+ panelClass
17
+ ),
18
+ children: [
19
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "bento-glow" }),
20
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "bento-edge" }),
21
+ children
22
+ ]
23
+ }
24
+ ) });
25
+ }
26
+ export {
27
+ BentoFrame
28
+ };
@@ -1,6 +1,10 @@
1
1
  import type { HTMLAttributes, ReactNode } from "react";
2
2
  export interface BentoGridProps extends HTMLAttributes<HTMLDivElement> {
3
3
  className?: string;
4
+ /** Tiles fade up in a stagger the first time the grid scrolls into view. */
5
+ reveal?: boolean;
6
+ /** Colour of the hover glow, the lit bottom edge, the icon ring and the CTA arrow. Any CSS colour; writes `--bento-accent`. */
7
+ accent?: string;
4
8
  children?: ReactNode;
5
9
  }
6
- export declare function BentoGrid({ className, children, ...rest }: BentoGridProps): import("react").JSX.Element;
10
+ export declare function BentoGrid({ className, reveal, accent, children, style, onFocus, ...rest }: BentoGridProps): import("react").JSX.Element;
@@ -1,14 +1,83 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
+ import { useState, useEffect } from "react";
3
4
  import { cn } from "../../utils.js";
4
- function BentoGrid({ className = "", children, ...rest }) {
5
+ import { useElementRef } from "../../internals/dom/use-element-ref.js";
6
+ import { useInView } from "../../internals/motion/in-view.js";
7
+ import { useReducedMotion } from "../../internals/motion/media-query.js";
8
+ import { staggerDelay } from "../../internals/motion/stagger.js";
9
+ /* empty css */
10
+ const STEP = 70;
11
+ const CAP = 420;
12
+ function BentoGrid({
13
+ className = "",
14
+ reveal = true,
15
+ accent,
16
+ children,
17
+ style,
18
+ onFocus,
19
+ ...rest
20
+ }) {
21
+ const [node, nodeRef] = useElementRef();
22
+ const [shown, setShown] = useState(false);
23
+ const reduced = useReducedMotion();
24
+ const revealing = reveal && !reduced;
25
+ useEffect(() => {
26
+ if (!node) return;
27
+ const root = node;
28
+ function apply() {
29
+ const kids = Array.from(root.children).filter(
30
+ (el) => el instanceof HTMLElement
31
+ );
32
+ kids.forEach((el, i) => {
33
+ el.style.setProperty("--bento-i", String(i));
34
+ el.style.setProperty(
35
+ "--bento-delay",
36
+ `${staggerDelay(i, kids.length, STEP, "first", CAP)}ms`
37
+ );
38
+ });
39
+ }
40
+ apply();
41
+ const mo = new MutationObserver(apply);
42
+ mo.observe(root, { childList: true });
43
+ return () => {
44
+ mo.disconnect();
45
+ for (const el of Array.from(root.children)) {
46
+ if (el instanceof HTMLElement) {
47
+ el.style.removeProperty("--bento-i");
48
+ el.style.removeProperty("--bento-delay");
49
+ }
50
+ }
51
+ };
52
+ }, [node]);
53
+ useInView(node, {
54
+ enabled: revealing && !shown,
55
+ once: true,
56
+ // Any pixel, not a fraction: a grid taller than ~12 viewports could never
57
+ // show 8% of itself at once, and would stay hidden for good.
58
+ threshold: 0,
59
+ rootMargin: "0px 0px -6% 0px",
60
+ onChange: (v) => {
61
+ if (v) setShown(true);
62
+ }
63
+ });
64
+ function handleFocus(event) {
65
+ setShown(true);
66
+ onFocus?.(event);
67
+ }
68
+ const mergedStyle = accent === void 0 ? style : { ...style, "--bento-accent": accent };
5
69
  return /* @__PURE__ */ jsx(
6
70
  "div",
7
71
  {
72
+ ref: nodeRef,
8
73
  className: cn(
9
- "mx-auto grid max-w-7xl grid-cols-1 gap-4 md:auto-rows-[18rem] md:grid-cols-3",
74
+ "fancy-bento mx-auto grid max-w-7xl grid-cols-1 gap-4 md:auto-rows-[18rem] md:grid-cols-3",
75
+ revealing && "bento-reveal",
10
76
  className
11
77
  ),
78
+ "data-state": revealing ? shown ? "shown" : "armed" : void 0,
79
+ style: mergedStyle,
80
+ onFocus: handleFocus,
12
81
  ...rest,
13
82
  children
14
83
  }
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsxs, jsx } from "react/jsx-runtime";
3
- import { cn } from "../../utils.js";
3
+ import { BentoFrame } from "./BentoFrame.js";
4
4
  function BentoGridCard({
5
5
  name,
6
6
  description: desc,
@@ -12,34 +12,42 @@ function BentoGridCard({
12
12
  ...rest
13
13
  }) {
14
14
  return /* @__PURE__ */ jsxs(
15
- "div",
15
+ BentoFrame,
16
16
  {
17
- className: cn(
18
- "group relative col-span-3 flex flex-col justify-end overflow-hidden rounded-xl",
19
- "bg-white [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)]",
20
- "transform-gpu dark:bg-black dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset] dark:[border:1px_solid_rgba(255,255,255,.1)]",
21
- className
22
- ),
17
+ className: ["group col-span-3", className].join(" "),
18
+ panelClass: "justify-end",
23
19
  ...rest,
24
20
  children: [
25
- background,
26
- /* @__PURE__ */ jsxs("div", { className: "pointer-events-none z-10 flex transform-gpu flex-col gap-1 p-6 transition-all duration-300 group-hover:-translate-y-10", children: [
27
- /* @__PURE__ */ jsx("div", { className: "size-12 origin-left transform-gpu text-neutral-700 transition-all duration-300 ease-in-out group-hover:scale-75", children: icon }),
28
- /* @__PURE__ */ jsx("h3", { className: "text-xl font-semibold text-neutral-700 dark:text-neutral-300", children: name }),
29
- /* @__PURE__ */ jsx("p", { className: "max-w-lg text-neutral-400", children: desc })
30
- ] }),
31
- /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute bottom-0 flex w-full translate-y-10 transform-gpu flex-row items-center p-4 opacity-0 transition-all duration-300 group-hover:translate-y-0 group-hover:opacity-100", children: /* @__PURE__ */ jsxs(
32
- "a",
33
- {
34
- href,
35
- className: "pointer-events-auto inline-flex items-center rounded-md px-3 py-1.5 text-sm font-medium text-neutral-700 transition-colors hover:bg-neutral-100 dark:text-neutral-200 dark:hover:bg-neutral-800",
36
- children: [
37
- cta,
38
- " →"
39
- ]
40
- }
41
- ) }),
42
- /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 transform-gpu transition-all duration-300 group-hover:bg-black/[.03] group-hover:dark:bg-neutral-800/10" })
21
+ background && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 -z-10 overflow-hidden", children: background }),
22
+ /* @__PURE__ */ jsxs("div", { className: "bento-lift pointer-events-none relative z-10 flex flex-col gap-1 p-6", children: [
23
+ icon && /* @__PURE__ */ jsx("div", { className: "bento-icon mb-3 size-11 [&_svg]:size-5", children: icon }),
24
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-medium tracking-tight text-neutral-900 dark:text-neutral-100", children: name }),
25
+ /* @__PURE__ */ jsx("p", { className: "max-w-lg text-sm leading-relaxed text-neutral-500 dark:text-neutral-400", children: desc }),
26
+ /* @__PURE__ */ jsxs(
27
+ "a",
28
+ {
29
+ href,
30
+ className: "bento-cta pointer-events-auto mt-3 inline-flex w-fit items-center gap-1.5 rounded-md text-sm font-medium text-neutral-900 outline-none focus-visible:ring-2 focus-visible:ring-[var(--bento-accent,#8e9cff)] focus-visible:ring-offset-2 focus-visible:ring-offset-transparent dark:text-neutral-100",
31
+ children: [
32
+ cta,
33
+ /* @__PURE__ */ jsx(
34
+ "svg",
35
+ {
36
+ "aria-hidden": "true",
37
+ viewBox: "0 0 16 16",
38
+ className: "size-3.5 text-[var(--bento-accent,#8e9cff)]",
39
+ fill: "none",
40
+ stroke: "currentColor",
41
+ strokeWidth: "1.6",
42
+ strokeLinecap: "round",
43
+ strokeLinejoin: "round",
44
+ children: /* @__PURE__ */ jsx("path", { d: "M3 8h9.5M8.5 4l4 4-4 4" })
45
+ }
46
+ )
47
+ ]
48
+ }
49
+ )
50
+ ] })
43
51
  ]
44
52
  }
45
53
  );
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { jsxs, jsx } from "react/jsx-runtime";
3
- import { cn } from "../../utils.js";
3
+ import { BentoFrame } from "./BentoFrame.js";
4
4
  function BentoGridItem({
5
5
  className = "",
6
6
  header,
@@ -10,19 +10,19 @@ function BentoGridItem({
10
10
  ...rest
11
11
  }) {
12
12
  return /* @__PURE__ */ jsxs(
13
- "div",
13
+ BentoFrame,
14
14
  {
15
- className: cn(
16
- "group/bento shadow-input row-span-1 flex flex-col justify-between space-y-4 rounded-xl border border-transparent bg-white p-4 transition duration-200 hover:shadow-xl dark:border-white/[0.2] dark:bg-black dark:shadow-none",
17
- className
18
- ),
15
+ className: ["group/bento row-span-1", className].join(" "),
16
+ panelClass: "justify-between gap-4 p-4",
19
17
  ...rest,
20
18
  children: [
21
19
  header,
22
- /* @__PURE__ */ jsxs("div", { className: "transition duration-200 group-hover/bento:translate-x-2", children: [
23
- icon,
24
- title && /* @__PURE__ */ jsx("div", { className: "my-2 font-sans font-bold text-neutral-600 dark:text-neutral-200", children: title }),
25
- description && /* @__PURE__ */ jsx("div", { className: "font-sans text-xs font-normal text-neutral-600 dark:text-neutral-300", children: description })
20
+ /* @__PURE__ */ jsxs("div", { className: "bento-lift relative", children: [
21
+ (icon || title) && /* @__PURE__ */ jsxs("div", { className: "mb-1.5 flex items-center gap-2.5", children: [
22
+ icon && /* @__PURE__ */ jsx("div", { className: "bento-icon size-8 [&_svg]:size-4", children: icon }),
23
+ title && /* @__PURE__ */ jsx("div", { className: "min-w-0 font-sans text-[15px] font-medium tracking-tight text-neutral-900 dark:text-neutral-100", children: title })
24
+ ] }),
25
+ description && /* @__PURE__ */ jsx("div", { className: "font-sans text-[13px] leading-relaxed text-neutral-500 dark:text-neutral-400", children: description })
26
26
  ] })
27
27
  ]
28
28
  }
@@ -1,25 +1,70 @@
1
1
  /**
2
- * BorderBeam - Animated beam effect that travels around the border
2
+ * BorderBeam - a single comet of light riding a container's border
3
3
  *
4
- * Place inside a container with `position: relative` and `overflow: hidden`.
5
- * The beam will animate around the container's border.
4
+ * A white-hot head leads a long, feathered tail that fades from `colorFrom`
5
+ * to `colorTo` to nothing. The hairline under it stays dim except within
6
+ * the comet's reach, a soft bloom sits on the edge, and a faint spill of
7
+ * light falls just inside the container.
8
+ *
9
+ * Place it as the last child of a container with `position: relative`
10
+ * (and usually `overflow: hidden`). It inherits the container's
11
+ * `border-radius`; set `--border-beam-radius` if the path needs a radius
12
+ * the browser cannot read from the box itself.
6
13
  */
7
14
  export interface BorderBeamProps {
8
15
  /** Additional CSS classes */
9
16
  className?: string;
10
- /** Size of the beam in pixels */
17
+ /** Diameter of the pool of light around the comet's head, in pixels */
11
18
  size?: number;
12
- /** Animation duration in seconds */
19
+ /** Time for one full lap of the border, in seconds */
13
20
  duration?: number;
14
21
  /** Border width in pixels */
15
22
  borderWidth?: number;
16
- /** Anchor position (0-100) */
23
+ /**
24
+ * Where along the border the comet starts, as a percentage of the
25
+ * perimeter (0-100, clockwise from the top-left corner). With reduced
26
+ * motion, this is where it rests.
27
+ */
17
28
  anchor?: number;
18
- /** Gradient start color */
29
+ /** Colour of the comet's head and the start of its tail */
19
30
  colorFrom?: string;
20
- /** Gradient end color */
31
+ /** Colour the tail fades into */
21
32
  colorTo?: string;
22
- /** Animation delay in seconds */
33
+ /**
34
+ * Delay in seconds. The comet runs from the first frame, already
35
+ * `delay` seconds behind where it would otherwise be, so stacked beams
36
+ * spread out along the border.
37
+ */
23
38
  delay?: number;
39
+ /** Length of the tail as a share of the perimeter (0-1) */
40
+ tail?: number;
41
+ /** Strength of the bloom and the inner spill of light (0-1) */
42
+ glow?: number;
43
+ /** Travel counter-clockwise instead of clockwise */
44
+ reverse?: boolean;
45
+ }
46
+ /** Number of overlapping pieces the sharp tail is drawn with. More pieces
47
+ * bend around the corners more smoothly. */
48
+ export declare const TAIL_SEGMENTS = 18;
49
+ /** The blurred bloom is forgiving: fewer, longer pieces are enough. */
50
+ export declare const BLOOM_SEGMENTS = 8;
51
+ export interface TailSegment {
52
+ /** Position along the tail, 0 = right behind the head, 1 = tail end */
53
+ t: number;
54
+ /** Opacity of this piece */
55
+ alpha: number;
56
+ /** Thickness factor (1 at the head, tapering towards the end) */
57
+ thickness: number;
24
58
  }
25
- export declare function BorderBeam({ className, size, duration, borderWidth, anchor, colorFrom, colorTo, delay, }: BorderBeamProps): import("react").JSX.Element;
59
+ /** Evenly spaced pieces with an eased fade and a tapering thickness. */
60
+ export declare function tailSegments(count: number): TailSegment[];
61
+ /**
62
+ * Turn `delay` into a phase: how far into its lap the comet is at first
63
+ * paint (a negative `animation-delay`), and where it rests when motion is
64
+ * reduced (a percentage of the perimeter).
65
+ */
66
+ export declare function beamPhase(duration: number, delay: number, anchor: number, reverse: boolean): {
67
+ offset: number;
68
+ park: number;
69
+ };
70
+ export declare function BorderBeam({ className, size, duration, borderWidth, anchor, colorFrom, colorTo, delay, tail, glow, reverse, }: BorderBeamProps): import("react").JSX.Element;