react-marketing-ui 0.0.4 → 0.0.6

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 CHANGED
@@ -10,6 +10,18 @@ Ported from the [offkeep](https://offkeep.com) marketing site into a standalone
10
10
  bun add react-marketing-ui
11
11
  ```
12
12
 
13
+ ### DiaTextReveal styles
14
+
15
+ Import the animation stylesheet once in your app's global stylesheet:
16
+
17
+ ```css
18
+ @import "react-marketing-ui/dia-text-reveal.css";
19
+ ```
20
+
21
+ The package build emits CSS separately from JavaScript. These keyframes are
22
+ required for the text reveal and for advancing repeated lines; scanning the
23
+ package with Tailwind's `@source` does not include them.
24
+
13
25
  ## Components
14
26
 
15
27
  - **SparklesText** — text wrapped in continuously respawning sparkle stars
@@ -0,0 +1,2 @@
1
+ export default function FooterGlow(): import("react").JSX.Element;
2
+ //# sourceMappingURL=footer-glow.d.ts.map
@@ -0,0 +1,2 @@
1
+ export default function FooterGlow(): import("react").JSX.Element;
2
+ //# sourceMappingURL=footer-glow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"footer-glow.d.ts","sourceRoot":"","sources":["../../src/components/footer-glow.tsx"],"names":[],"mappings":"AAwEA,MAAM,CAAC,OAAO,UAAU,UAAU,gCAmGjC"}
@@ -0,0 +1,110 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var framerMotion = require('framer-motion');
5
+ var react = require('react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ var VB_W = 1270;
9
+ var VB_H = 600;
10
+ var BAND_TOP = 0;
11
+ var OVERHANG = "0.5rem";
12
+ var OVERSHOOT = 60;
13
+ var BAND_X = -OVERSHOOT;
14
+ var BAND_W = VB_W + OVERSHOOT * 2;
15
+ var BAND_H = VB_H + OVERSHOOT;
16
+ var HUES = [188, 210, 232, 250, 262, 285, 308, 330, 350];
17
+ var ARCH_RX = VB_W * 1.35;
18
+ var FADE_STOPS = [
19
+ { offset: 0, opacity: 1 },
20
+ { offset: 0.62, opacity: 1 },
21
+ { offset: 0.81, opacity: 0.8 },
22
+ { offset: 0.93, opacity: 0.35 },
23
+ { offset: 1, opacity: 0 }
24
+ ];
25
+ var MAX_OPACITY = 0.85;
26
+ function FadeStops() {
27
+ return FADE_STOPS.map(({ offset, opacity }) => /* @__PURE__ */ jsxRuntime.jsx("stop", { offset, style: { stopColor: "#fff", stopOpacity: opacity } }, offset));
28
+ }
29
+ function FooterGlow() {
30
+ const ref = react.useRef(null);
31
+ const reduce = framerMotion.useReducedMotion();
32
+ const uid = react.useId();
33
+ const hueId = `footer-glow-hue-${uid}`;
34
+ const flatFadeId = `footer-glow-fade-flat-${uid}`;
35
+ const archFadeId = `footer-glow-fade-arch-${uid}`;
36
+ const flatMaskId = `footer-glow-mask-flat-${uid}`;
37
+ const archMaskId = `footer-glow-mask-arch-${uid}`;
38
+ const { scrollYProgress } = framerMotion.useScroll({ target: ref, offset: ["start end", "start center"] });
39
+ const y = framerMotion.useTransform(scrollYProgress, [0, 0.7, 1], ["55%", "5%", "2%"]);
40
+ const opacity = framerMotion.useTransform(scrollYProgress, [0, 0.65], [0, MAX_OPACITY]);
41
+ return /* @__PURE__ */ jsxRuntime.jsx(
42
+ "div",
43
+ {
44
+ ref,
45
+ "aria-hidden": "true",
46
+ className: "pointer-events-none absolute inset-x-0 bottom-0 overflow-hidden",
47
+ style: { height: `calc(100% + ${OVERHANG})` },
48
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
49
+ framerMotion.motion.svg,
50
+ {
51
+ viewBox: `0 0 ${VB_W} ${VB_H}`,
52
+ preserveAspectRatio: "none",
53
+ className: "h-full w-full",
54
+ overflow: "visible",
55
+ style: reduce ? { opacity: MAX_OPACITY } : { y, opacity },
56
+ children: [
57
+ /* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
58
+ /* @__PURE__ */ jsxRuntime.jsx("linearGradient", { id: hueId, x1: "0", y1: "0", x2: "1", y2: "0", children: HUES.map((hue, i) => /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: i / (HUES.length - 1), style: { stopColor: `hsl(${hue} 95% 55%)` } }, hue)) }),
59
+ /* @__PURE__ */ jsxRuntime.jsx("linearGradient", { id: flatFadeId, gradientUnits: "userSpaceOnUse", x1: "0", y1: VB_H, x2: "0", y2: BAND_TOP, children: /* @__PURE__ */ jsxRuntime.jsx(FadeStops, {}) }),
60
+ /* @__PURE__ */ jsxRuntime.jsx(
61
+ "radialGradient",
62
+ {
63
+ id: archFadeId,
64
+ gradientUnits: "userSpaceOnUse",
65
+ cx: VB_W / 2,
66
+ cy: VB_H,
67
+ r: VB_H - BAND_TOP,
68
+ gradientTransform: `translate(${VB_W / 2} 0) scale(${ARCH_RX / (VB_H - BAND_TOP)} 1) translate(${-VB_W / 2} 0)`,
69
+ children: /* @__PURE__ */ jsxRuntime.jsx(FadeStops, {})
70
+ }
71
+ ),
72
+ [
73
+ { id: flatMaskId, fill: flatFadeId },
74
+ { id: archMaskId, fill: archFadeId }
75
+ ].map(({ id, fill }) => /* @__PURE__ */ jsxRuntime.jsx("mask", { id, maskUnits: "userSpaceOnUse", x: BAND_X, y: "0", width: BAND_W, height: BAND_H, children: /* @__PURE__ */ jsxRuntime.jsx("rect", { x: BAND_X, y: "0", width: BAND_W, height: BAND_H, fill: `url(#${fill})` }) }, id))
76
+ ] }),
77
+ /* @__PURE__ */ jsxRuntime.jsx(
78
+ "rect",
79
+ {
80
+ x: BAND_X,
81
+ y: "0",
82
+ width: BAND_W,
83
+ height: BAND_H,
84
+ fill: `url(#${hueId})`,
85
+ mask: `url(#${flatMaskId})`,
86
+ className: "md:hidden"
87
+ }
88
+ ),
89
+ /* @__PURE__ */ jsxRuntime.jsx(
90
+ "rect",
91
+ {
92
+ x: BAND_X,
93
+ y: "0",
94
+ width: BAND_W,
95
+ height: BAND_H,
96
+ fill: `url(#${hueId})`,
97
+ mask: `url(#${archMaskId})`,
98
+ className: "hidden md:block"
99
+ }
100
+ )
101
+ ]
102
+ }
103
+ )
104
+ }
105
+ );
106
+ }
107
+
108
+ module.exports = FooterGlow;
109
+ //# sourceMappingURL=footer-glow.js.map
110
+ //# sourceMappingURL=footer-glow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/footer-glow.tsx"],"names":["jsx","useRef","useReducedMotion","useId","useScroll","useTransform","jsxs","motion"],"mappings":"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","file":"footer-glow.js","sourcesContent":["'use client';\n\nimport { motion, useReducedMotion, useScroll, useTransform } from 'framer-motion';\nimport { useId, useRef } from 'react';\n\n/**\n * Blurred brand glow behind the footer card — inspired by the Lovable footer.\n * A single full-bleed band carries a horizontal hue sweep (cyan → blue → brand\n * violet → pink) and is masked so it fades out towards the top: on wide screens\n * a very wide ellipse anchored to the bottom edge gives a shallow arch (solid to\n * the corners at the base, cresting in the middle); below `md` the same ramp runs\n * straight up, because at narrow widths the arch has no room to read as a curve\n * and just looks like a lopsided top edge. The wash reads as one even sheet of\n * colour with no bar seams and no hard cut against `overflow-hidden`. It fades +\n * rises into place as the footer scrolls into view (scroll-linked, not a\n * one-shot), and stays static when the user prefers reduced motion.\n *\n * Both masks are always in the markup and CSS picks between them, so the static\n * export is correct at every width without a JS width branch.\n */\n\nconst VB_W = 1270;\nconst VB_H = 600;\n// The band's top: the vertical fade reaches full transparency at the very top of\n// the layer, which itself overhangs the footer (see OVERHANG) — so the glow dies\n// out in open air above the footer rather than against a clipped edge.\nconst BAND_TOP = 0;\n// How far the glow layer reaches above the footer's top edge, into the CTA\n// section. Only the faint tail of the fade lands up there.\nconst OVERHANG = '0.5rem';\n// Overshoot — the band runs past the viewBox on the sides and below so the\n// colour stays full-bleed to the footer's edges even while the scroll transform\n// nudges it downwards.\nconst OVERSHOOT = 60;\nconst BAND_X = -OVERSHOOT;\nconst BAND_W = VB_W + OVERSHOOT * 2;\nconst BAND_H = VB_H + OVERSHOOT;\n\n// Hue sweep across the width. Saturation + lightness are constant so no column\n// reads brighter than its neighbours; only the hue moves.\nconst HUES = [188, 210, 232, 250, 262 /* centre — brand violet */, 285, 308, 330, 350];\n\n// Arch curvature, as a multiple of the band width: the horizontal radius of the\n// fade's ellipse. Larger = flatter. At 1.35 the outer edges give out ~7% of the\n// footer's height below the centre — a shallow crown, not a dome. The bottom\n// corners stay well inside the solid core, so the base is still edge-to-edge.\nconst ARCH_RX = VB_W * 1.35;\n\n// Alpha ramp from the solid base outwards (bottom → top, or centre-bottom → rim\n// of the ellipse for the arched variant). Solid through the lower two thirds,\n// then a tight falloff — holding full opacity that long is what keeps the band\n// reading as a defined sheet of colour instead of a diffuse haze. The ellipse's\n// rim lands exactly on BAND_TOP along the centre line, so both variants agree\n// down the middle; the arch only differs by the sides reaching zero sooner.\nconst FADE_STOPS = [\n { offset: 0, opacity: 1 },\n { offset: 0.62, opacity: 1 },\n { offset: 0.81, opacity: 0.8 },\n { offset: 0.93, opacity: 0.35 },\n { offset: 1, opacity: 0 },\n];\n\n// Peak wash opacity. The glow sits *behind* the opaque card (footer on top), so\n// only the part above the card shows — it can carry rich, saturated colour.\nconst MAX_OPACITY = 0.85;\n\nfunction FadeStops() {\n return FADE_STOPS.map(({ offset, opacity }) => (\n <stop key={offset} offset={offset} style={{ stopColor: '#fff', stopOpacity: opacity }} />\n ));\n}\n\nexport default function FooterGlow() {\n const ref = useRef<HTMLDivElement>(null);\n const reduce = useReducedMotion();\n const uid = useId();\n const hueId = `footer-glow-hue-${uid}`;\n const flatFadeId = `footer-glow-fade-flat-${uid}`;\n const archFadeId = `footer-glow-fade-arch-${uid}`;\n const flatMaskId = `footer-glow-mask-flat-${uid}`;\n const archMaskId = `footer-glow-mask-arch-${uid}`;\n\n // Progress runs 0 → 1 as the layer's top edge travels from the bottom of the\n // viewport to its middle. Deliberately viewport-relative rather than tied to\n // the footer's own height: the footer is several screens tall once the link\n // columns stack, and anchoring the end to it ('end end') meant the glow only\n // finished rising at the very bottom of the page on a phone. Half a viewport\n // of travel puts it in place early and behaves the same at every width.\n const { scrollYProgress } = useScroll({ target: ref, offset: ['start end', 'start center'] });\n\n const y = useTransform(scrollYProgress, [0, 0.7, 1], ['55%', '5%', '2%']);\n const opacity = useTransform(scrollYProgress, [0, 0.65], [0, MAX_OPACITY]);\n\n // Full-bleed background layer, anchored to the footer's bottom and one overhang\n // taller than it, so the band scales with the footer while spilling above its\n // top edge. It clips itself rather than relying on the footer: that keeps the\n // sides and the scroll-transformed bottom contained (no stray page overflow)\n // while leaving the top free. `pointer-events-none` keeps the CTA above it\n // clickable through the spill.\n return (\n <div\n ref={ref}\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute inset-x-0 bottom-0 overflow-hidden\"\n style={{ height: `calc(100% + ${OVERHANG})` }}\n >\n <motion.svg\n viewBox={`0 0 ${VB_W} ${VB_H}`}\n preserveAspectRatio=\"none\"\n className=\"h-full w-full\"\n overflow=\"visible\"\n style={reduce ? { opacity: MAX_OPACITY } : { y, opacity }}\n >\n <defs>\n <linearGradient id={hueId} x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\">\n {HUES.map((hue, i) => (\n <stop key={hue} offset={i / (HUES.length - 1)} style={{ stopColor: `hsl(${hue} 95% 55%)` }} />\n ))}\n </linearGradient>\n\n {/* Flat variant — a straight bottom-to-top ramp. */}\n <linearGradient id={flatFadeId} gradientUnits=\"userSpaceOnUse\" x1=\"0\" y1={VB_H} x2=\"0\" y2={BAND_TOP}>\n <FadeStops />\n </linearGradient>\n\n {/* Arched variant — a circle of radius (VB_H - BAND_TOP) centred on the\n bottom edge, stretched horizontally into a very wide ellipse; that\n stretch is the arch. Both use userSpaceOnUse so the geometry is\n absolute, independent of the rect's bounding box (which overshoots\n the viewBox on three sides). */}\n <radialGradient\n id={archFadeId}\n gradientUnits=\"userSpaceOnUse\"\n cx={VB_W / 2}\n cy={VB_H}\n r={VB_H - BAND_TOP}\n gradientTransform={`translate(${VB_W / 2} 0) scale(${ARCH_RX / (VB_H - BAND_TOP)} 1) translate(${-VB_W / 2} 0)`}\n >\n <FadeStops />\n </radialGradient>\n\n {[\n { id: flatMaskId, fill: flatFadeId },\n { id: archMaskId, fill: archFadeId },\n ].map(({ id, fill }) => (\n <mask key={id} id={id} maskUnits=\"userSpaceOnUse\" x={BAND_X} y=\"0\" width={BAND_W} height={BAND_H}>\n <rect x={BAND_X} y=\"0\" width={BAND_W} height={BAND_H} fill={`url(#${fill})`} />\n </mask>\n ))}\n </defs>\n <rect\n x={BAND_X}\n y=\"0\"\n width={BAND_W}\n height={BAND_H}\n fill={`url(#${hueId})`}\n mask={`url(#${flatMaskId})`}\n className=\"md:hidden\"\n />\n <rect\n x={BAND_X}\n y=\"0\"\n width={BAND_W}\n height={BAND_H}\n fill={`url(#${hueId})`}\n mask={`url(#${archMaskId})`}\n className=\"hidden md:block\"\n />\n </motion.svg>\n </div>\n );\n}\n"]}
@@ -0,0 +1,108 @@
1
+ "use client";
2
+ import { useReducedMotion, useScroll, useTransform, motion } from 'framer-motion';
3
+ import { useRef, useId } from 'react';
4
+ import { jsx, jsxs } from 'react/jsx-runtime';
5
+
6
+ var VB_W = 1270;
7
+ var VB_H = 600;
8
+ var BAND_TOP = 0;
9
+ var OVERHANG = "0.5rem";
10
+ var OVERSHOOT = 60;
11
+ var BAND_X = -OVERSHOOT;
12
+ var BAND_W = VB_W + OVERSHOOT * 2;
13
+ var BAND_H = VB_H + OVERSHOOT;
14
+ var HUES = [188, 210, 232, 250, 262, 285, 308, 330, 350];
15
+ var ARCH_RX = VB_W * 1.35;
16
+ var FADE_STOPS = [
17
+ { offset: 0, opacity: 1 },
18
+ { offset: 0.62, opacity: 1 },
19
+ { offset: 0.81, opacity: 0.8 },
20
+ { offset: 0.93, opacity: 0.35 },
21
+ { offset: 1, opacity: 0 }
22
+ ];
23
+ var MAX_OPACITY = 0.85;
24
+ function FadeStops() {
25
+ return FADE_STOPS.map(({ offset, opacity }) => /* @__PURE__ */ jsx("stop", { offset, style: { stopColor: "#fff", stopOpacity: opacity } }, offset));
26
+ }
27
+ function FooterGlow() {
28
+ const ref = useRef(null);
29
+ const reduce = useReducedMotion();
30
+ const uid = useId();
31
+ const hueId = `footer-glow-hue-${uid}`;
32
+ const flatFadeId = `footer-glow-fade-flat-${uid}`;
33
+ const archFadeId = `footer-glow-fade-arch-${uid}`;
34
+ const flatMaskId = `footer-glow-mask-flat-${uid}`;
35
+ const archMaskId = `footer-glow-mask-arch-${uid}`;
36
+ const { scrollYProgress } = useScroll({ target: ref, offset: ["start end", "start center"] });
37
+ const y = useTransform(scrollYProgress, [0, 0.7, 1], ["55%", "5%", "2%"]);
38
+ const opacity = useTransform(scrollYProgress, [0, 0.65], [0, MAX_OPACITY]);
39
+ return /* @__PURE__ */ jsx(
40
+ "div",
41
+ {
42
+ ref,
43
+ "aria-hidden": "true",
44
+ className: "pointer-events-none absolute inset-x-0 bottom-0 overflow-hidden",
45
+ style: { height: `calc(100% + ${OVERHANG})` },
46
+ children: /* @__PURE__ */ jsxs(
47
+ motion.svg,
48
+ {
49
+ viewBox: `0 0 ${VB_W} ${VB_H}`,
50
+ preserveAspectRatio: "none",
51
+ className: "h-full w-full",
52
+ overflow: "visible",
53
+ style: reduce ? { opacity: MAX_OPACITY } : { y, opacity },
54
+ children: [
55
+ /* @__PURE__ */ jsxs("defs", { children: [
56
+ /* @__PURE__ */ jsx("linearGradient", { id: hueId, x1: "0", y1: "0", x2: "1", y2: "0", children: HUES.map((hue, i) => /* @__PURE__ */ jsx("stop", { offset: i / (HUES.length - 1), style: { stopColor: `hsl(${hue} 95% 55%)` } }, hue)) }),
57
+ /* @__PURE__ */ jsx("linearGradient", { id: flatFadeId, gradientUnits: "userSpaceOnUse", x1: "0", y1: VB_H, x2: "0", y2: BAND_TOP, children: /* @__PURE__ */ jsx(FadeStops, {}) }),
58
+ /* @__PURE__ */ jsx(
59
+ "radialGradient",
60
+ {
61
+ id: archFadeId,
62
+ gradientUnits: "userSpaceOnUse",
63
+ cx: VB_W / 2,
64
+ cy: VB_H,
65
+ r: VB_H - BAND_TOP,
66
+ gradientTransform: `translate(${VB_W / 2} 0) scale(${ARCH_RX / (VB_H - BAND_TOP)} 1) translate(${-VB_W / 2} 0)`,
67
+ children: /* @__PURE__ */ jsx(FadeStops, {})
68
+ }
69
+ ),
70
+ [
71
+ { id: flatMaskId, fill: flatFadeId },
72
+ { id: archMaskId, fill: archFadeId }
73
+ ].map(({ id, fill }) => /* @__PURE__ */ jsx("mask", { id, maskUnits: "userSpaceOnUse", x: BAND_X, y: "0", width: BAND_W, height: BAND_H, children: /* @__PURE__ */ jsx("rect", { x: BAND_X, y: "0", width: BAND_W, height: BAND_H, fill: `url(#${fill})` }) }, id))
74
+ ] }),
75
+ /* @__PURE__ */ jsx(
76
+ "rect",
77
+ {
78
+ x: BAND_X,
79
+ y: "0",
80
+ width: BAND_W,
81
+ height: BAND_H,
82
+ fill: `url(#${hueId})`,
83
+ mask: `url(#${flatMaskId})`,
84
+ className: "md:hidden"
85
+ }
86
+ ),
87
+ /* @__PURE__ */ jsx(
88
+ "rect",
89
+ {
90
+ x: BAND_X,
91
+ y: "0",
92
+ width: BAND_W,
93
+ height: BAND_H,
94
+ fill: `url(#${hueId})`,
95
+ mask: `url(#${archMaskId})`,
96
+ className: "hidden md:block"
97
+ }
98
+ )
99
+ ]
100
+ }
101
+ )
102
+ }
103
+ );
104
+ }
105
+
106
+ export { FooterGlow as default };
107
+ //# sourceMappingURL=footer-glow.mjs.map
108
+ //# sourceMappingURL=footer-glow.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/footer-glow.tsx"],"names":[],"mappings":"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","file":"footer-glow.mjs","sourcesContent":["'use client';\n\nimport { motion, useReducedMotion, useScroll, useTransform } from 'framer-motion';\nimport { useId, useRef } from 'react';\n\n/**\n * Blurred brand glow behind the footer card — inspired by the Lovable footer.\n * A single full-bleed band carries a horizontal hue sweep (cyan → blue → brand\n * violet → pink) and is masked so it fades out towards the top: on wide screens\n * a very wide ellipse anchored to the bottom edge gives a shallow arch (solid to\n * the corners at the base, cresting in the middle); below `md` the same ramp runs\n * straight up, because at narrow widths the arch has no room to read as a curve\n * and just looks like a lopsided top edge. The wash reads as one even sheet of\n * colour with no bar seams and no hard cut against `overflow-hidden`. It fades +\n * rises into place as the footer scrolls into view (scroll-linked, not a\n * one-shot), and stays static when the user prefers reduced motion.\n *\n * Both masks are always in the markup and CSS picks between them, so the static\n * export is correct at every width without a JS width branch.\n */\n\nconst VB_W = 1270;\nconst VB_H = 600;\n// The band's top: the vertical fade reaches full transparency at the very top of\n// the layer, which itself overhangs the footer (see OVERHANG) — so the glow dies\n// out in open air above the footer rather than against a clipped edge.\nconst BAND_TOP = 0;\n// How far the glow layer reaches above the footer's top edge, into the CTA\n// section. Only the faint tail of the fade lands up there.\nconst OVERHANG = '0.5rem';\n// Overshoot — the band runs past the viewBox on the sides and below so the\n// colour stays full-bleed to the footer's edges even while the scroll transform\n// nudges it downwards.\nconst OVERSHOOT = 60;\nconst BAND_X = -OVERSHOOT;\nconst BAND_W = VB_W + OVERSHOOT * 2;\nconst BAND_H = VB_H + OVERSHOOT;\n\n// Hue sweep across the width. Saturation + lightness are constant so no column\n// reads brighter than its neighbours; only the hue moves.\nconst HUES = [188, 210, 232, 250, 262 /* centre — brand violet */, 285, 308, 330, 350];\n\n// Arch curvature, as a multiple of the band width: the horizontal radius of the\n// fade's ellipse. Larger = flatter. At 1.35 the outer edges give out ~7% of the\n// footer's height below the centre — a shallow crown, not a dome. The bottom\n// corners stay well inside the solid core, so the base is still edge-to-edge.\nconst ARCH_RX = VB_W * 1.35;\n\n// Alpha ramp from the solid base outwards (bottom → top, or centre-bottom → rim\n// of the ellipse for the arched variant). Solid through the lower two thirds,\n// then a tight falloff — holding full opacity that long is what keeps the band\n// reading as a defined sheet of colour instead of a diffuse haze. The ellipse's\n// rim lands exactly on BAND_TOP along the centre line, so both variants agree\n// down the middle; the arch only differs by the sides reaching zero sooner.\nconst FADE_STOPS = [\n { offset: 0, opacity: 1 },\n { offset: 0.62, opacity: 1 },\n { offset: 0.81, opacity: 0.8 },\n { offset: 0.93, opacity: 0.35 },\n { offset: 1, opacity: 0 },\n];\n\n// Peak wash opacity. The glow sits *behind* the opaque card (footer on top), so\n// only the part above the card shows — it can carry rich, saturated colour.\nconst MAX_OPACITY = 0.85;\n\nfunction FadeStops() {\n return FADE_STOPS.map(({ offset, opacity }) => (\n <stop key={offset} offset={offset} style={{ stopColor: '#fff', stopOpacity: opacity }} />\n ));\n}\n\nexport default function FooterGlow() {\n const ref = useRef<HTMLDivElement>(null);\n const reduce = useReducedMotion();\n const uid = useId();\n const hueId = `footer-glow-hue-${uid}`;\n const flatFadeId = `footer-glow-fade-flat-${uid}`;\n const archFadeId = `footer-glow-fade-arch-${uid}`;\n const flatMaskId = `footer-glow-mask-flat-${uid}`;\n const archMaskId = `footer-glow-mask-arch-${uid}`;\n\n // Progress runs 0 → 1 as the layer's top edge travels from the bottom of the\n // viewport to its middle. Deliberately viewport-relative rather than tied to\n // the footer's own height: the footer is several screens tall once the link\n // columns stack, and anchoring the end to it ('end end') meant the glow only\n // finished rising at the very bottom of the page on a phone. Half a viewport\n // of travel puts it in place early and behaves the same at every width.\n const { scrollYProgress } = useScroll({ target: ref, offset: ['start end', 'start center'] });\n\n const y = useTransform(scrollYProgress, [0, 0.7, 1], ['55%', '5%', '2%']);\n const opacity = useTransform(scrollYProgress, [0, 0.65], [0, MAX_OPACITY]);\n\n // Full-bleed background layer, anchored to the footer's bottom and one overhang\n // taller than it, so the band scales with the footer while spilling above its\n // top edge. It clips itself rather than relying on the footer: that keeps the\n // sides and the scroll-transformed bottom contained (no stray page overflow)\n // while leaving the top free. `pointer-events-none` keeps the CTA above it\n // clickable through the spill.\n return (\n <div\n ref={ref}\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute inset-x-0 bottom-0 overflow-hidden\"\n style={{ height: `calc(100% + ${OVERHANG})` }}\n >\n <motion.svg\n viewBox={`0 0 ${VB_W} ${VB_H}`}\n preserveAspectRatio=\"none\"\n className=\"h-full w-full\"\n overflow=\"visible\"\n style={reduce ? { opacity: MAX_OPACITY } : { y, opacity }}\n >\n <defs>\n <linearGradient id={hueId} x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\">\n {HUES.map((hue, i) => (\n <stop key={hue} offset={i / (HUES.length - 1)} style={{ stopColor: `hsl(${hue} 95% 55%)` }} />\n ))}\n </linearGradient>\n\n {/* Flat variant — a straight bottom-to-top ramp. */}\n <linearGradient id={flatFadeId} gradientUnits=\"userSpaceOnUse\" x1=\"0\" y1={VB_H} x2=\"0\" y2={BAND_TOP}>\n <FadeStops />\n </linearGradient>\n\n {/* Arched variant — a circle of radius (VB_H - BAND_TOP) centred on the\n bottom edge, stretched horizontally into a very wide ellipse; that\n stretch is the arch. Both use userSpaceOnUse so the geometry is\n absolute, independent of the rect's bounding box (which overshoots\n the viewBox on three sides). */}\n <radialGradient\n id={archFadeId}\n gradientUnits=\"userSpaceOnUse\"\n cx={VB_W / 2}\n cy={VB_H}\n r={VB_H - BAND_TOP}\n gradientTransform={`translate(${VB_W / 2} 0) scale(${ARCH_RX / (VB_H - BAND_TOP)} 1) translate(${-VB_W / 2} 0)`}\n >\n <FadeStops />\n </radialGradient>\n\n {[\n { id: flatMaskId, fill: flatFadeId },\n { id: archMaskId, fill: archFadeId },\n ].map(({ id, fill }) => (\n <mask key={id} id={id} maskUnits=\"userSpaceOnUse\" x={BAND_X} y=\"0\" width={BAND_W} height={BAND_H}>\n <rect x={BAND_X} y=\"0\" width={BAND_W} height={BAND_H} fill={`url(#${fill})`} />\n </mask>\n ))}\n </defs>\n <rect\n x={BAND_X}\n y=\"0\"\n width={BAND_W}\n height={BAND_H}\n fill={`url(#${hueId})`}\n mask={`url(#${flatMaskId})`}\n className=\"md:hidden\"\n />\n <rect\n x={BAND_X}\n y=\"0\"\n width={BAND_W}\n height={BAND_H}\n fill={`url(#${hueId})`}\n mask={`url(#${archMaskId})`}\n className=\"hidden md:block\"\n />\n </motion.svg>\n </div>\n );\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -15,6 +15,7 @@ export { FadeIn, Stagger, StaggerItem } from './components/fade-in.js';
15
15
  export type { FeatureCardProps } from './components/feature-card.js';
16
16
  export { FeatureCard } from './components/feature-card.js';
17
17
  export { ArrowLeft, ArrowRight, FlowDiamond, FlowNode, FlowPill, JoinArrow, SplitArrow, TurnDown, } from './components/flowchart-parts.js';
18
+ export { default as FooterGlow } from './components/footer-glow.js';
18
19
  export type { GlossyButtonProps } from './components/glossy-button.js';
19
20
  export { GlossyButton, glossyButtonVariants } from './components/glossy-button.js';
20
21
  export { default as Grainient } from './components/grainient.js';
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@ export { FadeIn, Stagger, StaggerItem } from './components/fade-in.js';
15
15
  export type { FeatureCardProps } from './components/feature-card.js';
16
16
  export { FeatureCard } from './components/feature-card.js';
17
17
  export { ArrowLeft, ArrowRight, FlowDiamond, FlowNode, FlowPill, JoinArrow, SplitArrow, TurnDown, } from './components/flowchart-parts.js';
18
+ export { default as FooterGlow } from './components/footer-glow.js';
18
19
  export type { GlossyButtonProps } from './components/glossy-button.js';
19
20
  export { GlossyButton, glossyButtonVariants } from './components/glossy-button.js';
20
21
  export { default as Grainient } from './components/grainient.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1185,6 +1185,105 @@ function FlowDiamond({ label, sub, className }) {
1185
1185
  ] })
1186
1186
  ] });
1187
1187
  }
1188
+ var VB_W = 1270;
1189
+ var VB_H = 600;
1190
+ var BAND_TOP = 0;
1191
+ var OVERHANG = "0.5rem";
1192
+ var OVERSHOOT = 60;
1193
+ var BAND_X = -OVERSHOOT;
1194
+ var BAND_W = VB_W + OVERSHOOT * 2;
1195
+ var BAND_H = VB_H + OVERSHOOT;
1196
+ var HUES = [188, 210, 232, 250, 262, 285, 308, 330, 350];
1197
+ var ARCH_RX = VB_W * 1.35;
1198
+ var FADE_STOPS = [
1199
+ { offset: 0, opacity: 1 },
1200
+ { offset: 0.62, opacity: 1 },
1201
+ { offset: 0.81, opacity: 0.8 },
1202
+ { offset: 0.93, opacity: 0.35 },
1203
+ { offset: 1, opacity: 0 }
1204
+ ];
1205
+ var MAX_OPACITY = 0.85;
1206
+ function FadeStops() {
1207
+ return FADE_STOPS.map(({ offset, opacity }) => /* @__PURE__ */ jsxRuntime.jsx("stop", { offset, style: { stopColor: "#fff", stopOpacity: opacity } }, offset));
1208
+ }
1209
+ function FooterGlow() {
1210
+ const ref = react.useRef(null);
1211
+ const reduce = framerMotion.useReducedMotion();
1212
+ const uid = react.useId();
1213
+ const hueId = `footer-glow-hue-${uid}`;
1214
+ const flatFadeId = `footer-glow-fade-flat-${uid}`;
1215
+ const archFadeId = `footer-glow-fade-arch-${uid}`;
1216
+ const flatMaskId = `footer-glow-mask-flat-${uid}`;
1217
+ const archMaskId = `footer-glow-mask-arch-${uid}`;
1218
+ const { scrollYProgress } = framerMotion.useScroll({ target: ref, offset: ["start end", "start center"] });
1219
+ const y = framerMotion.useTransform(scrollYProgress, [0, 0.7, 1], ["55%", "5%", "2%"]);
1220
+ const opacity = framerMotion.useTransform(scrollYProgress, [0, 0.65], [0, MAX_OPACITY]);
1221
+ return /* @__PURE__ */ jsxRuntime.jsx(
1222
+ "div",
1223
+ {
1224
+ ref,
1225
+ "aria-hidden": "true",
1226
+ className: "pointer-events-none absolute inset-x-0 bottom-0 overflow-hidden",
1227
+ style: { height: `calc(100% + ${OVERHANG})` },
1228
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1229
+ framerMotion.motion.svg,
1230
+ {
1231
+ viewBox: `0 0 ${VB_W} ${VB_H}`,
1232
+ preserveAspectRatio: "none",
1233
+ className: "h-full w-full",
1234
+ overflow: "visible",
1235
+ style: reduce ? { opacity: MAX_OPACITY } : { y, opacity },
1236
+ children: [
1237
+ /* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
1238
+ /* @__PURE__ */ jsxRuntime.jsx("linearGradient", { id: hueId, x1: "0", y1: "0", x2: "1", y2: "0", children: HUES.map((hue, i) => /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: i / (HUES.length - 1), style: { stopColor: `hsl(${hue} 95% 55%)` } }, hue)) }),
1239
+ /* @__PURE__ */ jsxRuntime.jsx("linearGradient", { id: flatFadeId, gradientUnits: "userSpaceOnUse", x1: "0", y1: VB_H, x2: "0", y2: BAND_TOP, children: /* @__PURE__ */ jsxRuntime.jsx(FadeStops, {}) }),
1240
+ /* @__PURE__ */ jsxRuntime.jsx(
1241
+ "radialGradient",
1242
+ {
1243
+ id: archFadeId,
1244
+ gradientUnits: "userSpaceOnUse",
1245
+ cx: VB_W / 2,
1246
+ cy: VB_H,
1247
+ r: VB_H - BAND_TOP,
1248
+ gradientTransform: `translate(${VB_W / 2} 0) scale(${ARCH_RX / (VB_H - BAND_TOP)} 1) translate(${-VB_W / 2} 0)`,
1249
+ children: /* @__PURE__ */ jsxRuntime.jsx(FadeStops, {})
1250
+ }
1251
+ ),
1252
+ [
1253
+ { id: flatMaskId, fill: flatFadeId },
1254
+ { id: archMaskId, fill: archFadeId }
1255
+ ].map(({ id, fill }) => /* @__PURE__ */ jsxRuntime.jsx("mask", { id, maskUnits: "userSpaceOnUse", x: BAND_X, y: "0", width: BAND_W, height: BAND_H, children: /* @__PURE__ */ jsxRuntime.jsx("rect", { x: BAND_X, y: "0", width: BAND_W, height: BAND_H, fill: `url(#${fill})` }) }, id))
1256
+ ] }),
1257
+ /* @__PURE__ */ jsxRuntime.jsx(
1258
+ "rect",
1259
+ {
1260
+ x: BAND_X,
1261
+ y: "0",
1262
+ width: BAND_W,
1263
+ height: BAND_H,
1264
+ fill: `url(#${hueId})`,
1265
+ mask: `url(#${flatMaskId})`,
1266
+ className: "md:hidden"
1267
+ }
1268
+ ),
1269
+ /* @__PURE__ */ jsxRuntime.jsx(
1270
+ "rect",
1271
+ {
1272
+ x: BAND_X,
1273
+ y: "0",
1274
+ width: BAND_W,
1275
+ height: BAND_H,
1276
+ fill: `url(#${hueId})`,
1277
+ mask: `url(#${archMaskId})`,
1278
+ className: "hidden md:block"
1279
+ }
1280
+ )
1281
+ ]
1282
+ }
1283
+ )
1284
+ }
1285
+ );
1286
+ }
1188
1287
  var glossLayer = cn(
1189
1288
  "pointer-events-none absolute inset-0 z-[-1] rounded-[inherit]",
1190
1289
  "[background-color:var(--btn-face)] [box-shadow:var(--shadow-button)]",
@@ -2672,6 +2771,7 @@ exports.FeatureCard = FeatureCard;
2672
2771
  exports.FlowDiamond = FlowDiamond;
2673
2772
  exports.FlowNode = FlowNode;
2674
2773
  exports.FlowPill = FlowPill;
2774
+ exports.FooterGlow = FooterGlow;
2675
2775
  exports.GlossyButton = GlossyButton;
2676
2776
  exports.Grainient = grainient_default;
2677
2777
  exports.Highlighter = Highlighter;