@protohiro/effects 0.1.2 → 0.2.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.
package/README.md CHANGED
@@ -24,6 +24,7 @@ export function Button() {
24
24
  - `useGradientBorderEffect`
25
25
  - `useGlowEffect`
26
26
  - `useNoiseEffect`
27
+ - `useSpotlightEffect`
27
28
 
28
29
  Live demo:
29
30
  https://libs.protohiro.com/effects/
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- 'use strict';var effectsCore=require('@protohiro/effects-core'),react=require('react');var t=`
1
+ 'use strict';var effectsCore=require('@protohiro/effects-core'),react=require('react');var a=`
2
2
  .pe-gradient-border {
3
3
  position: relative;
4
4
  isolation: isolate;
@@ -65,10 +65,109 @@
65
65
  pointer-events: none;
66
66
  }
67
67
 
68
+ .pe-spotlight {
69
+ position: relative;
70
+ isolation: isolate;
71
+ }
72
+
73
+ .pe-spotlight::before {
74
+ content: '';
75
+ position: absolute;
76
+ inset: 0;
77
+ border-radius: inherit;
78
+ pointer-events: none;
79
+ opacity: var(--pe-spotlight-intensity, 0.45);
80
+ background:
81
+ radial-gradient(
82
+ circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),
83
+ color-mix(
84
+ in srgb,
85
+ var(--pe-spotlight-color, #7dd3fc) calc(var(--pe-spotlight-core-intensity, 0.58) * 100%),
86
+ white
87
+ )
88
+ 0%,
89
+ color-mix(
90
+ in srgb,
91
+ var(--pe-spotlight-color, #7dd3fc) calc((0.48 + var(--pe-spotlight-core-intensity, 0.58) * 0.32) * 100%),
92
+ white
93
+ )
94
+ calc(var(--pe-spotlight-size, 180px) * 0.14),
95
+ color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 38%, transparent) calc(var(--pe-spotlight-size, 180px) * 0.4),
96
+ color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 20%, transparent) var(--pe-spotlight-size, 180px),
97
+ transparent calc(var(--pe-spotlight-size, 180px) * 1.45)
98
+ ),
99
+ radial-gradient(
100
+ circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),
101
+ color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 16%, transparent) 0%,
102
+ color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 8%, transparent) calc(var(--pe-spotlight-size, 180px) * 1.2),
103
+ transparent calc(var(--pe-spotlight-size, 180px) * 1.8)
104
+ );
105
+ mix-blend-mode: screen;
106
+ filter: blur(calc(1px + var(--pe-spotlight-softness, 0.72) * 6px)) saturate(128%);
107
+ transition: background 120ms ease-out, opacity 120ms ease-out;
108
+ }
109
+
110
+ @supports ((-webkit-mask-image: radial-gradient(white, black)) or (mask-image: radial-gradient(white, black))) {
111
+ .pe-spotlight.pe-spotlight--reveal::before {
112
+ opacity: var(--pe-spotlight-reveal-opacity, 0.92);
113
+ background-image:
114
+ var(
115
+ --pe-spotlight-reveal-image,
116
+ url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 220'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0%25' stop-color='%236d28d9'/%3E%3Cstop offset='52%25' stop-color='%230ea5e9'/%3E%3Cstop offset='100%25' stop-color='%2322d3ee'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='320' height='220' fill='url(%23g)'/%3E%3Ccircle cx='86' cy='74' r='52' fill='rgba(255,255,255,0.24)'/%3E%3Ccircle cx='252' cy='154' r='70' fill='rgba(255,255,255,0.14)'/%3E%3C/svg%3E")
117
+ ),
118
+ linear-gradient(var(--pe-spotlight-reveal-color, #38bdf8), var(--pe-spotlight-reveal-color, #38bdf8));
119
+ background-size: var(--pe-spotlight-reveal-size, cover), cover;
120
+ background-position: center;
121
+ background-repeat: no-repeat;
122
+ mix-blend-mode: normal;
123
+ filter: saturate(120%);
124
+ -webkit-mask-image: radial-gradient(
125
+ circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),
126
+ rgba(0, 0, 0, 1) 0,
127
+ rgba(0, 0, 0, 1) calc(var(--pe-spotlight-size, 180px) * 0.36),
128
+ rgba(0, 0, 0, 0.75) calc(var(--pe-spotlight-size, 180px) * 0.72),
129
+ transparent calc(var(--pe-spotlight-size, 180px) * 1.22)
130
+ );
131
+ mask-image: radial-gradient(
132
+ circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),
133
+ rgba(0, 0, 0, 1) 0,
134
+ rgba(0, 0, 0, 1) calc(var(--pe-spotlight-size, 180px) * 0.36),
135
+ rgba(0, 0, 0, 0.75) calc(var(--pe-spotlight-size, 180px) * 0.72),
136
+ transparent calc(var(--pe-spotlight-size, 180px) * 1.22)
137
+ );
138
+ }
139
+ }
140
+
141
+ @supports not (color: color-mix(in srgb, white, black)) {
142
+ .pe-spotlight::before {
143
+ background:
144
+ radial-gradient(
145
+ circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),
146
+ rgba(255, 255, 255, 0.86) 0%,
147
+ rgba(255, 255, 255, 0.56) calc(var(--pe-spotlight-size, 180px) * 0.14),
148
+ rgba(255, 255, 255, 0.3) calc(var(--pe-spotlight-size, 180px) * 0.4),
149
+ rgba(255, 255, 255, 0.16) var(--pe-spotlight-size, 180px),
150
+ transparent calc(var(--pe-spotlight-size, 180px) * 1.45)
151
+ ),
152
+ radial-gradient(
153
+ circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),
154
+ rgba(255, 255, 255, 0.16) 0%,
155
+ rgba(255, 255, 255, 0.08) calc(var(--pe-spotlight-size, 180px) * 1.2),
156
+ transparent calc(var(--pe-spotlight-size, 180px) * 1.8)
157
+ );
158
+ }
159
+
160
+ .pe-spotlight.pe-spotlight--reveal::before {
161
+ background-image:
162
+ var(--pe-spotlight-reveal-image, linear-gradient(135deg, rgba(109, 40, 217, 0.9), rgba(14, 165, 233, 0.95))),
163
+ linear-gradient(var(--pe-spotlight-reveal-color, #38bdf8), var(--pe-spotlight-reveal-color, #38bdf8));
164
+ }
165
+ }
166
+
68
167
  @media (prefers-reduced-motion: reduce) {
69
168
  .pe-noise::after {
70
169
  opacity: calc(var(--pe-noise-opacity, 0.28) * 0.9);
71
170
  }
72
171
  }
73
- `;function o(e){let[r,a]=react.useState(null);return react.useEffect(()=>{if(!r||e.disabled)return;effectsCore.ensureGlobalStyle(e.styleId,e.styleText);let s=[];return s.push(effectsCore.applyEffectClass(r,e.className)),e.userClassName&&s.push(effectsCore.applyEffectClass(r,e.userClassName)),s.push(effectsCore.applyCssVars(r,e.vars)),()=>{for(let d of s.reverse())d();}},[r,e.className,e.userClassName,e.disabled,e.styleId,e.styleText,e.vars]),react.useCallback(s=>{a(s);},[])}var x="protohiro-effects-base";function E(e={}){let r=react.useMemo(()=>({"--pe-gb-thickness":effectsCore.toCssLength(e.thickness),"--pe-gb-radius":effectsCore.toCssLength(e.radius),"--pe-gb-colors":e.colors,"--pe-gb-angle":effectsCore.toCssAngle(e.angle)}),[e.thickness,e.radius,e.colors,e.angle]);return o({className:"pe-gradient-border",userClassName:e.className,vars:r,disabled:e.disabled,styleId:x,styleText:t})}var T="protohiro-effects-base";function h(e={}){let r=react.useMemo(()=>({"--pe-glow-color":e.color,"--pe-glow-blur":effectsCore.toCssLength(e.blur),"--pe-glow-spread":effectsCore.toCssLength(e.spread),"--pe-glow-opacity":effectsCore.toCssNumber(e.opacity)}),[e.color,e.blur,e.spread,e.opacity]);return o({className:"pe-glow",userClassName:e.className,vars:r,disabled:e.disabled,styleId:T,styleText:t})}var k="protohiro-effects-base";function L(e){if(e!==void 0&&!Number.isNaN(e))return Math.max(0,Math.min(1,e))}function S(e={}){let r=L(e.intensity),a=react.useMemo(()=>({"--pe-noise-size":effectsCore.toCssLength(e.size??120),"--pe-noise-opacity":effectsCore.toCssNumber(r??.2)}),[e.size,r]);return o({className:"pe-noise",userClassName:e.className,vars:a,disabled:e.disabled,styleId:k,styleText:t})}exports.useGlowEffect=h;exports.useGradientBorderEffect=E;exports.useNoiseEffect=S;//# sourceMappingURL=index.cjs.map
172
+ `;function i(e){let[t,o]=react.useState(null);return react.useEffect(()=>{if(!t||e.disabled)return;effectsCore.ensureGlobalStyle(e.styleId,e.styleText);let s=[];return s.push(effectsCore.applyEffectClass(t,e.className)),e.userClassName&&s.push(effectsCore.applyEffectClass(t,e.userClassName)),s.push(effectsCore.applyCssVars(t,e.vars)),()=>{for(let l of s.reverse())l();}},[t,e.className,e.userClassName,e.disabled,e.styleId,e.styleText,e.vars]),react.useCallback(s=>{o(s);},[])}var G="protohiro-effects-base";function F(e={}){let t=react.useMemo(()=>({"--pe-gb-thickness":effectsCore.toCssLength(e.thickness),"--pe-gb-radius":effectsCore.toCssLength(e.radius),"--pe-gb-colors":e.colors,"--pe-gb-angle":effectsCore.toCssAngle(e.angle)}),[e.thickness,e.radius,e.colors,e.angle]);return i({className:"pe-gradient-border",userClassName:e.className,vars:t,disabled:e.disabled,styleId:G,styleText:a})}var _="protohiro-effects-base";function B(e={}){let t=react.useMemo(()=>({"--pe-glow-color":e.color,"--pe-glow-blur":effectsCore.toCssLength(e.blur),"--pe-glow-spread":effectsCore.toCssLength(e.spread),"--pe-glow-opacity":effectsCore.toCssNumber(e.opacity)}),[e.color,e.blur,e.spread,e.opacity]);return i({className:"pe-glow",userClassName:e.className,vars:t,disabled:e.disabled,styleId:_,styleText:a})}var W="protohiro-effects-base";function $(e){if(e!==void 0&&!Number.isNaN(e))return Math.max(0,Math.min(1,e))}function A(e={}){let t=$(e.intensity),o=react.useMemo(()=>({"--pe-noise-size":effectsCore.toCssLength(e.size??120),"--pe-noise-opacity":effectsCore.toCssNumber(t??.2)}),[e.size,t]);return i({className:"pe-noise",userClassName:e.className,vars:o,disabled:e.disabled,styleId:W,styleText:a})}var J="protohiro-effects-base";function c(e){if(!(e===void 0||Number.isNaN(e)))return Math.max(0,Math.min(1,e))}function k(e){if(e!==void 0)return typeof e=="number"?`${e}%`:e}function K(e){if(!e)return;let t=e.trim();if(t)return t.startsWith("url(")||t.startsWith("linear-gradient(")||t.startsWith("radial-gradient(")||t.startsWith("conic-gradient(")?t:`url("${t.replace(/"/g,'\\"')}")`}function Q(e){return e===void 0||typeof e!="number"?e:Math.max(0,e)}function Z(e={}){let[t,o]=react.useState(null),s=e.mode==="reveal",l=Q(e.size),g=c(e.intensity),f=c(e.softness),m=c(e.coreIntensity),u=c(e.revealOpacity),b=k(e.x),h=k(e.y),v=K(e.revealImage),x=react.useMemo(()=>({"--pe-spotlight-size":effectsCore.toCssLength(l),"--pe-spotlight-intensity":effectsCore.toCssNumber(g),"--pe-spotlight-color":e.color,"--pe-spotlight-softness":effectsCore.toCssNumber(f),"--pe-spotlight-core-intensity":effectsCore.toCssNumber(m),"--pe-spotlight-x":b,"--pe-spotlight-y":h,"--pe-spotlight-reveal-color":e.revealColor,"--pe-spotlight-reveal-image":v,"--pe-spotlight-reveal-size":effectsCore.toCssLength(e.revealSize),"--pe-spotlight-reveal-opacity":effectsCore.toCssNumber(u)}),[l,g,f,m,e.color,b,h,e.revealColor,v,e.revealSize,u]);return react.useEffect(()=>{if(!t||e.disabled)return;effectsCore.ensureGlobalStyle(J,a);let r=[];return r.push(effectsCore.applyEffectClass(t,"pe-spotlight")),s&&r.push(effectsCore.applyEffectClass(t,"pe-spotlight--reveal")),e.className&&r.push(effectsCore.applyEffectClass(t,e.className)),r.push(effectsCore.applyCssVars(t,x)),()=>{for(let n of r.reverse())n();}},[t,e.disabled,e.className,s,x]),react.useEffect(()=>{if(!t||e.disabled||!e.followPointer)return;let r=n=>{t.style.setProperty("--pe-spotlight-x",`${n.offsetX}px`),t.style.setProperty("--pe-spotlight-y",`${n.offsetY}px`);};return t.addEventListener("pointermove",r),()=>{t.removeEventListener("pointermove",r);}},[t,e.disabled,e.followPointer]),react.useCallback(r=>{o(r);},[])}exports.useGlowEffect=B;exports.useGradientBorderEffect=F;exports.useNoiseEffect=A;exports.useSpotlightEffect=Z;//# sourceMappingURL=index.cjs.map
74
173
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/effectStyles.ts","../src/shared/useCssEffect.ts","../src/effects/useGradientBorderEffect.ts","../src/effects/useGlowEffect.ts","../src/effects/useNoiseEffect.ts"],"names":["EFFECT_STYLES","useCssEffect","input","node","setNode","useState","useEffect","ensureGlobalStyle","cleanups","applyEffectClass","applyCssVars","cleanup","useCallback","next","STYLE_ID","useGradientBorderEffect","options","vars","useMemo","toCssLength","toCssAngle","useGlowEffect","toCssNumber","clampUnit","value","useNoiseEffect","intensity"],"mappings":"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","file":"index.cjs","sourcesContent":["export const EFFECT_STYLES = `\n.pe-gradient-border {\n position: relative;\n isolation: isolate;\n border-radius: var(--pe-gb-radius, inherit);\n border-color: transparent;\n}\n\n.pe-gradient-border::before {\n content: '';\n position: absolute;\n z-index: -1;\n inset: calc(var(--pe-gb-thickness, 2px) * -1);\n border-radius: calc(var(--pe-gb-radius, 0px) + var(--pe-gb-thickness, 2px));\n box-sizing: border-box;\n padding: var(--pe-gb-thickness, 2px);\n background: linear-gradient(var(--pe-gb-angle, 120deg), var(--pe-gb-colors, #5eead4, #0ea5e9));\n pointer-events: none;\n\n /* Hollow out the center so only the ring remains. */\n -webkit-mask:\n linear-gradient(#fff 0 0) content-box,\n linear-gradient(#fff 0 0);\n -webkit-mask-composite: xor;\n mask:\n linear-gradient(#fff 0 0) content-box,\n linear-gradient(#fff 0 0);\n mask-composite: exclude;\n}\n\n@supports not ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {\n .pe-gradient-border::before {\n z-index: -1;\n inset: calc(var(--pe-gb-thickness, 2px) * -1);\n border-radius: calc(var(--pe-gb-radius, 0px) + var(--pe-gb-thickness, 2px));\n padding: 0;\n border: var(--pe-gb-thickness, 2px) solid;\n border-image: linear-gradient(var(--pe-gb-angle, 120deg), var(--pe-gb-colors, #5eead4, #0ea5e9)) 1;\n }\n}\n\n.pe-glow {\n box-shadow:\n 0 0 var(--pe-glow-blur, 24px)\n color-mix(in srgb, var(--pe-glow-color, #38bdf8) calc(var(--pe-glow-opacity, 0.4) * 100%), transparent),\n 0 0 var(--pe-glow-spread, 2px)\n color-mix(in srgb, var(--pe-glow-color, #38bdf8) calc(var(--pe-glow-opacity, 0.2) * 100%), transparent);\n}\n\n.pe-noise {\n position: relative;\n}\n\n.pe-noise::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160 160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.95' numOctaves='2' seed='13' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E\");\n background-size: var(--pe-noise-size, 80px) var(--pe-noise-size, 80px);\n background-repeat: repeat;\n image-rendering: pixelated;\n filter: contrast(190%) brightness(110%);\n opacity: var(--pe-noise-opacity, 0.28);\n pointer-events: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .pe-noise::after {\n opacity: calc(var(--pe-noise-opacity, 0.28) * 0.9);\n }\n}\n`;\n","import { applyCssVars, applyEffectClass, ensureGlobalStyle } from '@protohiro/effects-core';\nimport { useCallback, useEffect, useState } from 'react';\n\ntype UseCssEffectInput = {\n className: string;\n userClassName?: string;\n vars: Record<string, string | undefined>;\n disabled?: boolean;\n styleId: string;\n styleText: string;\n};\n\nexport function useCssEffect<T extends HTMLElement>(input: UseCssEffectInput) {\n const [node, setNode] = useState<T | null>(null);\n\n useEffect(() => {\n if (!node || input.disabled) {\n return;\n }\n\n ensureGlobalStyle(input.styleId, input.styleText);\n\n const cleanups: Array<() => void> = [];\n cleanups.push(applyEffectClass(node, input.className));\n\n if (input.userClassName) {\n cleanups.push(applyEffectClass(node, input.userClassName));\n }\n\n cleanups.push(applyCssVars(node, input.vars));\n\n return () => {\n for (const cleanup of cleanups.reverse()) {\n cleanup();\n }\n };\n }, [\n node,\n input.className,\n input.userClassName,\n input.disabled,\n input.styleId,\n input.styleText,\n input.vars,\n ]);\n\n return useCallback((next: T | null) => {\n setNode(next);\n }, []);\n}\n","import { toCssAngle, toCssLength } from '@protohiro/effects-core';\nimport { useMemo } from 'react';\n\nimport type { GradientBorderOptions } from '../types';\nimport { EFFECT_STYLES } from '../shared/effectStyles';\nimport { useCssEffect } from '../shared/useCssEffect';\n\nconst STYLE_ID = 'protohiro-effects-base';\n\nexport function useGradientBorderEffect<T extends HTMLElement = HTMLElement>(\n options: GradientBorderOptions = {},\n) {\n const vars = useMemo(\n () => ({\n '--pe-gb-thickness': toCssLength(options.thickness),\n '--pe-gb-radius': toCssLength(options.radius),\n '--pe-gb-colors': options.colors,\n '--pe-gb-angle': toCssAngle(options.angle),\n }),\n [options.thickness, options.radius, options.colors, options.angle],\n );\n\n return useCssEffect<T>({\n className: 'pe-gradient-border',\n userClassName: options.className,\n vars,\n disabled: options.disabled,\n styleId: STYLE_ID,\n styleText: EFFECT_STYLES,\n });\n}\n","import { toCssLength, toCssNumber } from '@protohiro/effects-core';\nimport { useMemo } from 'react';\n\nimport type { GlowOptions } from '../types';\nimport { EFFECT_STYLES } from '../shared/effectStyles';\nimport { useCssEffect } from '../shared/useCssEffect';\n\nconst STYLE_ID = 'protohiro-effects-base';\n\nexport function useGlowEffect<T extends HTMLElement = HTMLElement>(options: GlowOptions = {}) {\n const vars = useMemo(\n () => ({\n '--pe-glow-color': options.color,\n '--pe-glow-blur': toCssLength(options.blur),\n '--pe-glow-spread': toCssLength(options.spread),\n '--pe-glow-opacity': toCssNumber(options.opacity),\n }),\n [options.color, options.blur, options.spread, options.opacity],\n );\n\n return useCssEffect<T>({\n className: 'pe-glow',\n userClassName: options.className,\n vars,\n disabled: options.disabled,\n styleId: STYLE_ID,\n styleText: EFFECT_STYLES,\n });\n}\n","import { toCssLength, toCssNumber } from '@protohiro/effects-core';\nimport { useMemo } from 'react';\n\nimport type { NoiseOptions } from '../types';\nimport { EFFECT_STYLES } from '../shared/effectStyles';\nimport { useCssEffect } from '../shared/useCssEffect';\n\nconst STYLE_ID = 'protohiro-effects-base';\n\nfunction clampUnit(value: number | undefined): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (Number.isNaN(value)) {\n return undefined;\n }\n\n return Math.max(0, Math.min(1, value));\n}\n\nexport function useNoiseEffect<T extends HTMLElement = HTMLElement>(options: NoiseOptions = {}) {\n const intensity = clampUnit(options.intensity);\n\n const vars = useMemo(\n () => ({\n '--pe-noise-size': toCssLength(options.size ?? 120),\n '--pe-noise-opacity': toCssNumber(intensity ?? 0.2),\n }),\n [options.size, intensity],\n );\n\n return useCssEffect<T>({\n className: 'pe-noise',\n userClassName: options.className,\n vars,\n disabled: options.disabled,\n styleId: STYLE_ID,\n styleText: EFFECT_STYLES,\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/shared/effectStyles.ts","../src/shared/useCssEffect.ts","../src/effects/useGradientBorderEffect.ts","../src/effects/useGlowEffect.ts","../src/effects/useNoiseEffect.ts","../src/effects/useSpotlightEffect.ts"],"names":["EFFECT_STYLES","useCssEffect","input","node","setNode","useState","useEffect","ensureGlobalStyle","cleanups","applyEffectClass","applyCssVars","cleanup","useCallback","next","STYLE_ID","useGradientBorderEffect","options","vars","useMemo","toCssLength","toCssAngle","useGlowEffect","toCssNumber","clampUnit","value","useNoiseEffect","intensity","toCssPosition","toCssImage","trimmed","normalizeSpotlightSize","useSpotlightEffect","isRevealMode","size","softness","coreIntensity","revealOpacity","x","y","revealImage","onPointerMove","event"],"mappings":"uFAAO,IAAMA,CAAAA,CAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;ACA7B,CAAA,CAYO,SAASC,CAAAA,CAAoCC,EAA0B,CAC5E,GAAM,CAACC,CAAAA,CAAMC,CAAO,CAAA,CAAIC,cAAAA,CAAmB,IAAI,CAAA,CAE/C,OAAAC,gBAAU,IAAM,CACd,GAAI,CAACH,CAAAA,EAAQD,EAAM,QAAA,CACjB,OAGFK,8BAAkBL,CAAAA,CAAM,OAAA,CAASA,EAAM,SAAS,CAAA,CAEhD,IAAMM,CAAAA,CAA8B,EAAC,CACrC,OAAAA,EAAS,IAAA,CAAKC,4BAAAA,CAAiBN,EAAMD,CAAAA,CAAM,SAAS,CAAC,CAAA,CAEjDA,EAAM,aAAA,EACRM,CAAAA,CAAS,KAAKC,4BAAAA,CAAiBN,CAAAA,CAAMD,EAAM,aAAa,CAAC,CAAA,CAG3DM,CAAAA,CAAS,KAAKE,wBAAAA,CAAaP,CAAAA,CAAMD,EAAM,IAAI,CAAC,EAErC,IAAM,CACX,QAAWS,CAAAA,IAAWH,CAAAA,CAAS,SAAQ,CACrCG,CAAAA,GAEJ,CACF,CAAA,CAAG,CACDR,CAAAA,CACAD,CAAAA,CAAM,SAAA,CACNA,CAAAA,CAAM,cACNA,CAAAA,CAAM,QAAA,CACNA,EAAM,OAAA,CACNA,CAAAA,CAAM,UACNA,CAAAA,CAAM,IACR,CAAC,CAAA,CAEMU,kBAAaC,CAAAA,EAAmB,CACrCT,EAAQS,CAAI,EACd,EAAG,EAAE,CACP,CC1CA,IAAMC,CAAAA,CAAW,wBAAA,CAEV,SAASC,CAAAA,CACdC,CAAAA,CAAiC,EAAC,CAClC,CACA,IAAMC,CAAAA,CAAOC,aAAAA,CACX,KAAO,CACL,mBAAA,CAAqBC,wBAAYH,CAAAA,CAAQ,SAAS,EAClD,gBAAA,CAAkBG,uBAAAA,CAAYH,CAAAA,CAAQ,MAAM,EAC5C,gBAAA,CAAkBA,CAAAA,CAAQ,OAC1B,eAAA,CAAiBI,sBAAAA,CAAWJ,EAAQ,KAAK,CAC3C,CAAA,CAAA,CACA,CAACA,EAAQ,SAAA,CAAWA,CAAAA,CAAQ,OAAQA,CAAAA,CAAQ,MAAA,CAAQA,EAAQ,KAAK,CACnE,CAAA,CAEA,OAAOf,EAAgB,CACrB,SAAA,CAAW,qBACX,aAAA,CAAee,CAAAA,CAAQ,UACvB,IAAA,CAAAC,CAAAA,CACA,SAAUD,CAAAA,CAAQ,QAAA,CAClB,QAASF,CAAAA,CACT,SAAA,CAAWd,CACb,CAAC,CACH,CCvBA,IAAMc,CAAAA,CAAW,wBAAA,CAEV,SAASO,CAAAA,CAAmDL,EAAuB,EAAC,CAAG,CAC5F,IAAMC,CAAAA,CAAOC,cACX,KAAO,CACL,iBAAA,CAAmBF,CAAAA,CAAQ,MAC3B,gBAAA,CAAkBG,uBAAAA,CAAYH,EAAQ,IAAI,CAAA,CAC1C,mBAAoBG,uBAAAA,CAAYH,CAAAA,CAAQ,MAAM,CAAA,CAC9C,mBAAA,CAAqBM,wBAAYN,CAAAA,CAAQ,OAAO,CAClD,CAAA,CAAA,CACA,CAACA,EAAQ,KAAA,CAAOA,CAAAA,CAAQ,IAAA,CAAMA,CAAAA,CAAQ,OAAQA,CAAAA,CAAQ,OAAO,CAC/D,CAAA,CAEA,OAAOf,EAAgB,CACrB,SAAA,CAAW,SAAA,CACX,aAAA,CAAee,EAAQ,SAAA,CACvB,IAAA,CAAAC,EACA,QAAA,CAAUD,CAAAA,CAAQ,SAClB,OAAA,CAASF,CAAAA,CACT,SAAA,CAAWd,CACb,CAAC,CACH,CCrBA,IAAMc,CAAAA,CAAW,yBAEjB,SAASS,CAAAA,CAAUC,EAA+C,CAChE,GAAIA,CAAAA,GAAU,MAAA,EAIV,QAAO,KAAA,CAAMA,CAAK,EAItB,OAAO,IAAA,CAAK,IAAI,CAAA,CAAG,IAAA,CAAK,GAAA,CAAI,CAAA,CAAGA,CAAK,CAAC,CACvC,CAEO,SAASC,CAAAA,CAAoDT,EAAwB,EAAC,CAAG,CAC9F,IAAMU,EAAYH,CAAAA,CAAUP,CAAAA,CAAQ,SAAS,CAAA,CAEvCC,CAAAA,CAAOC,cACX,KAAO,CACL,kBAAmBC,uBAAAA,CAAYH,CAAAA,CAAQ,MAAQ,GAAG,CAAA,CAClD,qBAAsBM,uBAAAA,CAAYI,CAAAA,EAAa,EAAG,CACpD,CAAA,CAAA,CACA,CAACV,CAAAA,CAAQ,KAAMU,CAAS,CAC1B,EAEA,OAAOzB,CAAAA,CAAgB,CACrB,SAAA,CAAW,UAAA,CACX,aAAA,CAAee,CAAAA,CAAQ,UACvB,IAAA,CAAAC,CAAAA,CACA,SAAUD,CAAAA,CAAQ,QAAA,CAClB,QAASF,CAAAA,CACT,SAAA,CAAWd,CACb,CAAC,CACH,CC5BA,IAAMc,CAAAA,CAAW,yBAEjB,SAASS,CAAAA,CAAUC,EAA+C,CAChE,GAAI,EAAAA,CAAAA,GAAU,QAAa,MAAA,CAAO,KAAA,CAAMA,CAAK,CAAA,CAAA,CAI7C,OAAO,KAAK,GAAA,CAAI,CAAA,CAAG,IAAA,CAAK,GAAA,CAAI,EAAGA,CAAK,CAAC,CACvC,CAEA,SAASG,EAAcH,CAAAA,CAAwD,CAC7E,GAAIA,CAAAA,GAAU,MAAA,CAId,OAAO,OAAOA,CAAAA,EAAU,SAAW,CAAA,EAAGA,CAAK,IAAMA,CACnD,CAEA,SAASI,CAAAA,CAAWJ,EAA+C,CACjE,GAAI,CAACA,CAAAA,CACH,OAGF,IAAMK,CAAAA,CAAUL,CAAAA,CAAM,IAAA,EAAK,CAC3B,GAAKK,CAAAA,CAIL,OACEA,EAAQ,UAAA,CAAW,MAAM,GACzBA,CAAAA,CAAQ,UAAA,CAAW,kBAAkB,CAAA,EACrCA,EAAQ,UAAA,CAAW,kBAAkB,GACrCA,CAAAA,CAAQ,UAAA,CAAW,iBAAiB,CAAA,CAE7BA,CAAAA,CAGF,QAAQA,CAAAA,CAAQ,OAAA,CAAQ,KAAM,KAAK,CAAC,IAC7C,CAEA,SAASC,EACPN,CAAAA,CAC6B,CAC7B,OAAIA,CAAAA,GAAU,QAAa,OAAOA,CAAAA,EAAU,SACnCA,CAAAA,CAGF,IAAA,CAAK,IAAI,CAAA,CAAGA,CAAK,CAC1B,CAEO,SAASO,CAAAA,CACdf,CAAAA,CAA4B,EAAC,CAC7B,CACA,GAAM,CAACb,CAAAA,CAAMC,CAAO,CAAA,CAAIC,eAAmB,IAAI,CAAA,CACzC2B,EAAehB,CAAAA,CAAQ,IAAA,GAAS,SAChCiB,CAAAA,CAAOH,CAAAA,CAAuBd,EAAQ,IAAI,CAAA,CAC1CU,EAAYH,CAAAA,CAAUP,CAAAA,CAAQ,SAAS,CAAA,CACvCkB,CAAAA,CAAWX,EAAUP,CAAAA,CAAQ,QAAQ,CAAA,CACrCmB,CAAAA,CAAgBZ,EAAUP,CAAAA,CAAQ,aAAa,EAC/CoB,CAAAA,CAAgBb,CAAAA,CAAUP,EAAQ,aAAa,CAAA,CAC/CqB,CAAAA,CAAIV,CAAAA,CAAcX,EAAQ,CAAC,CAAA,CAC3BsB,EAAIX,CAAAA,CAAcX,CAAAA,CAAQ,CAAC,CAAA,CAC3BuB,CAAAA,CAAcX,CAAAA,CAAWZ,CAAAA,CAAQ,WAAW,CAAA,CAE5CC,CAAAA,CAAOC,cACX,KAAO,CACL,sBAAuBC,uBAAAA,CAAYc,CAAI,EACvC,0BAAA,CAA4BX,uBAAAA,CAAYI,CAAS,CAAA,CACjD,sBAAA,CAAwBV,EAAQ,KAAA,CAChC,yBAAA,CAA2BM,wBAAYY,CAAQ,CAAA,CAC/C,+BAAA,CAAiCZ,uBAAAA,CAAYa,CAAa,CAAA,CAC1D,kBAAA,CAAoBE,EACpB,kBAAA,CAAoBC,CAAAA,CACpB,8BAA+BtB,CAAAA,CAAQ,WAAA,CACvC,6BAAA,CAA+BuB,CAAAA,CAC/B,6BAA8BpB,uBAAAA,CAAYH,CAAAA,CAAQ,UAAU,CAAA,CAC5D,+BAAA,CAAiCM,wBAAYc,CAAa,CAC5D,CAAA,CAAA,CACA,CACEH,EACAP,CAAAA,CACAQ,CAAAA,CACAC,EACAnB,CAAAA,CAAQ,KAAA,CACRqB,EACAC,CAAAA,CACAtB,CAAAA,CAAQ,YACRuB,CAAAA,CACAvB,CAAAA,CAAQ,WACRoB,CACF,CACF,EAEA,OAAA9B,eAAAA,CAAU,IAAM,CACd,GAAI,CAACH,CAAAA,EAAQa,EAAQ,QAAA,CACnB,OAGFT,8BAAkBO,CAAAA,CAAUd,CAAa,EAEzC,IAAMQ,CAAAA,CAA8B,EAAC,CACrC,OAAAA,CAAAA,CAAS,IAAA,CAAKC,6BAAiBN,CAAAA,CAAM,cAAc,CAAC,CAAA,CAChD6B,CAAAA,EACFxB,CAAAA,CAAS,IAAA,CAAKC,6BAAiBN,CAAAA,CAAM,sBAAsB,CAAC,CAAA,CAG1Da,CAAAA,CAAQ,WACVR,CAAAA,CAAS,IAAA,CAAKC,6BAAiBN,CAAAA,CAAMa,CAAAA,CAAQ,SAAS,CAAC,CAAA,CAGzDR,EAAS,IAAA,CAAKE,wBAAAA,CAAaP,EAAMc,CAAI,CAAC,CAAA,CAE/B,IAAM,CACX,IAAA,IAAWN,CAAAA,IAAWH,EAAS,OAAA,EAAQ,CACrCG,IAEJ,CACF,CAAA,CAAG,CAACR,EAAMa,CAAAA,CAAQ,QAAA,CAAUA,EAAQ,SAAA,CAAWgB,CAAAA,CAAcf,CAAI,CAAC,CAAA,CAElEX,eAAAA,CAAU,IAAM,CACd,GAAI,CAACH,GAAQa,CAAAA,CAAQ,QAAA,EAAY,CAACA,CAAAA,CAAQ,aAAA,CACxC,OAGF,IAAMwB,CAAAA,CAAiBC,GAAwB,CAC7CtC,CAAAA,CAAK,MAAM,WAAA,CAAY,kBAAA,CAAoB,GAAGsC,CAAAA,CAAM,OAAO,CAAA,EAAA,CAAI,CAAA,CAC/DtC,EAAK,KAAA,CAAM,WAAA,CAAY,mBAAoB,CAAA,EAAGsC,CAAAA,CAAM,OAAO,CAAA,EAAA,CAAI,EACjE,EAEA,OAAAtC,CAAAA,CAAK,iBAAiB,aAAA,CAAeqC,CAAa,EAE3C,IAAM,CACXrC,EAAK,mBAAA,CAAoB,aAAA,CAAeqC,CAAa,EACvD,CACF,CAAA,CAAG,CAACrC,EAAMa,CAAAA,CAAQ,QAAA,CAAUA,EAAQ,aAAa,CAAC,EAE3CJ,iBAAAA,CAAaC,CAAAA,EAAmB,CACrCT,CAAAA,CAAQS,CAAI,EACd,CAAA,CAAG,EAAE,CACP","file":"index.cjs","sourcesContent":["export const EFFECT_STYLES = `\n.pe-gradient-border {\n position: relative;\n isolation: isolate;\n border-radius: var(--pe-gb-radius, inherit);\n border-color: transparent;\n}\n\n.pe-gradient-border::before {\n content: '';\n position: absolute;\n z-index: -1;\n inset: calc(var(--pe-gb-thickness, 2px) * -1);\n border-radius: calc(var(--pe-gb-radius, 0px) + var(--pe-gb-thickness, 2px));\n box-sizing: border-box;\n padding: var(--pe-gb-thickness, 2px);\n background: linear-gradient(var(--pe-gb-angle, 120deg), var(--pe-gb-colors, #5eead4, #0ea5e9));\n pointer-events: none;\n\n /* Hollow out the center so only the ring remains. */\n -webkit-mask:\n linear-gradient(#fff 0 0) content-box,\n linear-gradient(#fff 0 0);\n -webkit-mask-composite: xor;\n mask:\n linear-gradient(#fff 0 0) content-box,\n linear-gradient(#fff 0 0);\n mask-composite: exclude;\n}\n\n@supports not ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {\n .pe-gradient-border::before {\n z-index: -1;\n inset: calc(var(--pe-gb-thickness, 2px) * -1);\n border-radius: calc(var(--pe-gb-radius, 0px) + var(--pe-gb-thickness, 2px));\n padding: 0;\n border: var(--pe-gb-thickness, 2px) solid;\n border-image: linear-gradient(var(--pe-gb-angle, 120deg), var(--pe-gb-colors, #5eead4, #0ea5e9)) 1;\n }\n}\n\n.pe-glow {\n box-shadow:\n 0 0 var(--pe-glow-blur, 24px)\n color-mix(in srgb, var(--pe-glow-color, #38bdf8) calc(var(--pe-glow-opacity, 0.4) * 100%), transparent),\n 0 0 var(--pe-glow-spread, 2px)\n color-mix(in srgb, var(--pe-glow-color, #38bdf8) calc(var(--pe-glow-opacity, 0.2) * 100%), transparent);\n}\n\n.pe-noise {\n position: relative;\n}\n\n.pe-noise::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160 160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.95' numOctaves='2' seed='13' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E\");\n background-size: var(--pe-noise-size, 80px) var(--pe-noise-size, 80px);\n background-repeat: repeat;\n image-rendering: pixelated;\n filter: contrast(190%) brightness(110%);\n opacity: var(--pe-noise-opacity, 0.28);\n pointer-events: none;\n}\n\n.pe-spotlight {\n position: relative;\n isolation: isolate;\n}\n\n.pe-spotlight::before {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n opacity: var(--pe-spotlight-intensity, 0.45);\n background:\n radial-gradient(\n circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),\n color-mix(\n in srgb,\n var(--pe-spotlight-color, #7dd3fc) calc(var(--pe-spotlight-core-intensity, 0.58) * 100%),\n white\n )\n 0%,\n color-mix(\n in srgb,\n var(--pe-spotlight-color, #7dd3fc) calc((0.48 + var(--pe-spotlight-core-intensity, 0.58) * 0.32) * 100%),\n white\n )\n calc(var(--pe-spotlight-size, 180px) * 0.14),\n color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 38%, transparent) calc(var(--pe-spotlight-size, 180px) * 0.4),\n color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 20%, transparent) var(--pe-spotlight-size, 180px),\n transparent calc(var(--pe-spotlight-size, 180px) * 1.45)\n ),\n radial-gradient(\n circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),\n color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 16%, transparent) 0%,\n color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 8%, transparent) calc(var(--pe-spotlight-size, 180px) * 1.2),\n transparent calc(var(--pe-spotlight-size, 180px) * 1.8)\n );\n mix-blend-mode: screen;\n filter: blur(calc(1px + var(--pe-spotlight-softness, 0.72) * 6px)) saturate(128%);\n transition: background 120ms ease-out, opacity 120ms ease-out;\n}\n\n@supports ((-webkit-mask-image: radial-gradient(white, black)) or (mask-image: radial-gradient(white, black))) {\n .pe-spotlight.pe-spotlight--reveal::before {\n opacity: var(--pe-spotlight-reveal-opacity, 0.92);\n background-image:\n var(\n --pe-spotlight-reveal-image,\n url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 220'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0%25' stop-color='%236d28d9'/%3E%3Cstop offset='52%25' stop-color='%230ea5e9'/%3E%3Cstop offset='100%25' stop-color='%2322d3ee'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='320' height='220' fill='url(%23g)'/%3E%3Ccircle cx='86' cy='74' r='52' fill='rgba(255,255,255,0.24)'/%3E%3Ccircle cx='252' cy='154' r='70' fill='rgba(255,255,255,0.14)'/%3E%3C/svg%3E\")\n ),\n linear-gradient(var(--pe-spotlight-reveal-color, #38bdf8), var(--pe-spotlight-reveal-color, #38bdf8));\n background-size: var(--pe-spotlight-reveal-size, cover), cover;\n background-position: center;\n background-repeat: no-repeat;\n mix-blend-mode: normal;\n filter: saturate(120%);\n -webkit-mask-image: radial-gradient(\n circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),\n rgba(0, 0, 0, 1) 0,\n rgba(0, 0, 0, 1) calc(var(--pe-spotlight-size, 180px) * 0.36),\n rgba(0, 0, 0, 0.75) calc(var(--pe-spotlight-size, 180px) * 0.72),\n transparent calc(var(--pe-spotlight-size, 180px) * 1.22)\n );\n mask-image: radial-gradient(\n circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),\n rgba(0, 0, 0, 1) 0,\n rgba(0, 0, 0, 1) calc(var(--pe-spotlight-size, 180px) * 0.36),\n rgba(0, 0, 0, 0.75) calc(var(--pe-spotlight-size, 180px) * 0.72),\n transparent calc(var(--pe-spotlight-size, 180px) * 1.22)\n );\n }\n}\n\n@supports not (color: color-mix(in srgb, white, black)) {\n .pe-spotlight::before {\n background:\n radial-gradient(\n circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),\n rgba(255, 255, 255, 0.86) 0%,\n rgba(255, 255, 255, 0.56) calc(var(--pe-spotlight-size, 180px) * 0.14),\n rgba(255, 255, 255, 0.3) calc(var(--pe-spotlight-size, 180px) * 0.4),\n rgba(255, 255, 255, 0.16) var(--pe-spotlight-size, 180px),\n transparent calc(var(--pe-spotlight-size, 180px) * 1.45)\n ),\n radial-gradient(\n circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),\n rgba(255, 255, 255, 0.16) 0%,\n rgba(255, 255, 255, 0.08) calc(var(--pe-spotlight-size, 180px) * 1.2),\n transparent calc(var(--pe-spotlight-size, 180px) * 1.8)\n );\n }\n\n .pe-spotlight.pe-spotlight--reveal::before {\n background-image:\n var(--pe-spotlight-reveal-image, linear-gradient(135deg, rgba(109, 40, 217, 0.9), rgba(14, 165, 233, 0.95))),\n linear-gradient(var(--pe-spotlight-reveal-color, #38bdf8), var(--pe-spotlight-reveal-color, #38bdf8));\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .pe-noise::after {\n opacity: calc(var(--pe-noise-opacity, 0.28) * 0.9);\n }\n}\n`;\n","import { applyCssVars, applyEffectClass, ensureGlobalStyle } from '@protohiro/effects-core';\nimport { useCallback, useEffect, useState } from 'react';\n\ntype UseCssEffectInput = {\n className: string;\n userClassName?: string;\n vars: Record<string, string | undefined>;\n disabled?: boolean;\n styleId: string;\n styleText: string;\n};\n\nexport function useCssEffect<T extends HTMLElement>(input: UseCssEffectInput) {\n const [node, setNode] = useState<T | null>(null);\n\n useEffect(() => {\n if (!node || input.disabled) {\n return;\n }\n\n ensureGlobalStyle(input.styleId, input.styleText);\n\n const cleanups: Array<() => void> = [];\n cleanups.push(applyEffectClass(node, input.className));\n\n if (input.userClassName) {\n cleanups.push(applyEffectClass(node, input.userClassName));\n }\n\n cleanups.push(applyCssVars(node, input.vars));\n\n return () => {\n for (const cleanup of cleanups.reverse()) {\n cleanup();\n }\n };\n }, [\n node,\n input.className,\n input.userClassName,\n input.disabled,\n input.styleId,\n input.styleText,\n input.vars,\n ]);\n\n return useCallback((next: T | null) => {\n setNode(next);\n }, []);\n}\n","import { toCssAngle, toCssLength } from '@protohiro/effects-core';\nimport { useMemo } from 'react';\n\nimport type { GradientBorderOptions } from '../types';\nimport { EFFECT_STYLES } from '../shared/effectStyles';\nimport { useCssEffect } from '../shared/useCssEffect';\n\nconst STYLE_ID = 'protohiro-effects-base';\n\nexport function useGradientBorderEffect<T extends HTMLElement = HTMLElement>(\n options: GradientBorderOptions = {},\n) {\n const vars = useMemo(\n () => ({\n '--pe-gb-thickness': toCssLength(options.thickness),\n '--pe-gb-radius': toCssLength(options.radius),\n '--pe-gb-colors': options.colors,\n '--pe-gb-angle': toCssAngle(options.angle),\n }),\n [options.thickness, options.radius, options.colors, options.angle],\n );\n\n return useCssEffect<T>({\n className: 'pe-gradient-border',\n userClassName: options.className,\n vars,\n disabled: options.disabled,\n styleId: STYLE_ID,\n styleText: EFFECT_STYLES,\n });\n}\n","import { toCssLength, toCssNumber } from '@protohiro/effects-core';\nimport { useMemo } from 'react';\n\nimport type { GlowOptions } from '../types';\nimport { EFFECT_STYLES } from '../shared/effectStyles';\nimport { useCssEffect } from '../shared/useCssEffect';\n\nconst STYLE_ID = 'protohiro-effects-base';\n\nexport function useGlowEffect<T extends HTMLElement = HTMLElement>(options: GlowOptions = {}) {\n const vars = useMemo(\n () => ({\n '--pe-glow-color': options.color,\n '--pe-glow-blur': toCssLength(options.blur),\n '--pe-glow-spread': toCssLength(options.spread),\n '--pe-glow-opacity': toCssNumber(options.opacity),\n }),\n [options.color, options.blur, options.spread, options.opacity],\n );\n\n return useCssEffect<T>({\n className: 'pe-glow',\n userClassName: options.className,\n vars,\n disabled: options.disabled,\n styleId: STYLE_ID,\n styleText: EFFECT_STYLES,\n });\n}\n","import { toCssLength, toCssNumber } from '@protohiro/effects-core';\nimport { useMemo } from 'react';\n\nimport type { NoiseOptions } from '../types';\nimport { EFFECT_STYLES } from '../shared/effectStyles';\nimport { useCssEffect } from '../shared/useCssEffect';\n\nconst STYLE_ID = 'protohiro-effects-base';\n\nfunction clampUnit(value: number | undefined): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (Number.isNaN(value)) {\n return undefined;\n }\n\n return Math.max(0, Math.min(1, value));\n}\n\nexport function useNoiseEffect<T extends HTMLElement = HTMLElement>(options: NoiseOptions = {}) {\n const intensity = clampUnit(options.intensity);\n\n const vars = useMemo(\n () => ({\n '--pe-noise-size': toCssLength(options.size ?? 120),\n '--pe-noise-opacity': toCssNumber(intensity ?? 0.2),\n }),\n [options.size, intensity],\n );\n\n return useCssEffect<T>({\n className: 'pe-noise',\n userClassName: options.className,\n vars,\n disabled: options.disabled,\n styleId: STYLE_ID,\n styleText: EFFECT_STYLES,\n });\n}\n","import {\n applyCssVars,\n applyEffectClass,\n ensureGlobalStyle,\n toCssLength,\n toCssNumber,\n} from '@protohiro/effects-core';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport type { SpotlightOptions } from '../types';\nimport { EFFECT_STYLES } from '../shared/effectStyles';\n\nconst STYLE_ID = 'protohiro-effects-base';\n\nfunction clampUnit(value: number | undefined): number | undefined {\n if (value === undefined || Number.isNaN(value)) {\n return undefined;\n }\n\n return Math.max(0, Math.min(1, value));\n}\n\nfunction toCssPosition(value: string | number | undefined): string | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n return typeof value === 'number' ? `${value}%` : value;\n}\n\nfunction toCssImage(value: string | undefined): string | undefined {\n if (!value) {\n return undefined;\n }\n\n const trimmed = value.trim();\n if (!trimmed) {\n return undefined;\n }\n\n if (\n trimmed.startsWith('url(') ||\n trimmed.startsWith('linear-gradient(') ||\n trimmed.startsWith('radial-gradient(') ||\n trimmed.startsWith('conic-gradient(')\n ) {\n return trimmed;\n }\n\n return `url(\"${trimmed.replace(/\"/g, '\\\\\"')}\")`;\n}\n\nfunction normalizeSpotlightSize(\n value: string | number | undefined,\n): string | number | undefined {\n if (value === undefined || typeof value !== 'number') {\n return value;\n }\n\n return Math.max(0, value);\n}\n\nexport function useSpotlightEffect<T extends HTMLElement = HTMLElement>(\n options: SpotlightOptions = {},\n) {\n const [node, setNode] = useState<T | null>(null);\n const isRevealMode = options.mode === 'reveal';\n const size = normalizeSpotlightSize(options.size);\n const intensity = clampUnit(options.intensity);\n const softness = clampUnit(options.softness);\n const coreIntensity = clampUnit(options.coreIntensity);\n const revealOpacity = clampUnit(options.revealOpacity);\n const x = toCssPosition(options.x);\n const y = toCssPosition(options.y);\n const revealImage = toCssImage(options.revealImage);\n\n const vars = useMemo(\n () => ({\n '--pe-spotlight-size': toCssLength(size),\n '--pe-spotlight-intensity': toCssNumber(intensity),\n '--pe-spotlight-color': options.color,\n '--pe-spotlight-softness': toCssNumber(softness),\n '--pe-spotlight-core-intensity': toCssNumber(coreIntensity),\n '--pe-spotlight-x': x,\n '--pe-spotlight-y': y,\n '--pe-spotlight-reveal-color': options.revealColor,\n '--pe-spotlight-reveal-image': revealImage,\n '--pe-spotlight-reveal-size': toCssLength(options.revealSize),\n '--pe-spotlight-reveal-opacity': toCssNumber(revealOpacity),\n }),\n [\n size,\n intensity,\n softness,\n coreIntensity,\n options.color,\n x,\n y,\n options.revealColor,\n revealImage,\n options.revealSize,\n revealOpacity,\n ],\n );\n\n useEffect(() => {\n if (!node || options.disabled) {\n return;\n }\n\n ensureGlobalStyle(STYLE_ID, EFFECT_STYLES);\n\n const cleanups: Array<() => void> = [];\n cleanups.push(applyEffectClass(node, 'pe-spotlight'));\n if (isRevealMode) {\n cleanups.push(applyEffectClass(node, 'pe-spotlight--reveal'));\n }\n\n if (options.className) {\n cleanups.push(applyEffectClass(node, options.className));\n }\n\n cleanups.push(applyCssVars(node, vars));\n\n return () => {\n for (const cleanup of cleanups.reverse()) {\n cleanup();\n }\n };\n }, [node, options.disabled, options.className, isRevealMode, vars]);\n\n useEffect(() => {\n if (!node || options.disabled || !options.followPointer) {\n return;\n }\n\n const onPointerMove = (event: PointerEvent) => {\n node.style.setProperty('--pe-spotlight-x', `${event.offsetX}px`);\n node.style.setProperty('--pe-spotlight-y', `${event.offsetY}px`);\n };\n\n node.addEventListener('pointermove', onPointerMove);\n\n return () => {\n node.removeEventListener('pointermove', onPointerMove);\n };\n }, [node, options.disabled, options.followPointer]);\n\n return useCallback((next: T | null) => {\n setNode(next);\n }, []);\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -18,6 +18,21 @@ interface NoiseOptions extends EffectBaseOptions {
18
18
  size?: string | number;
19
19
  intensity?: number;
20
20
  }
21
+ interface SpotlightOptions extends EffectBaseOptions {
22
+ mode?: 'glow' | 'reveal';
23
+ size?: string | number;
24
+ intensity?: number;
25
+ color?: string;
26
+ softness?: number;
27
+ coreIntensity?: number;
28
+ x?: string | number;
29
+ y?: string | number;
30
+ followPointer?: boolean;
31
+ revealColor?: string;
32
+ revealImage?: string;
33
+ revealSize?: string | number;
34
+ revealOpacity?: number;
35
+ }
21
36
 
22
37
  declare function useGradientBorderEffect<T extends HTMLElement = HTMLElement>(options?: GradientBorderOptions): (next: T | null) => void;
23
38
 
@@ -25,4 +40,6 @@ declare function useGlowEffect<T extends HTMLElement = HTMLElement>(options?: Gl
25
40
 
26
41
  declare function useNoiseEffect<T extends HTMLElement = HTMLElement>(options?: NoiseOptions): (next: T | null) => void;
27
42
 
28
- export { type EffectBaseOptions, type GlowOptions, type GradientBorderOptions, type NoiseOptions, useGlowEffect, useGradientBorderEffect, useNoiseEffect };
43
+ declare function useSpotlightEffect<T extends HTMLElement = HTMLElement>(options?: SpotlightOptions): (next: T | null) => void;
44
+
45
+ export { type EffectBaseOptions, type GlowOptions, type GradientBorderOptions, type NoiseOptions, type SpotlightOptions, useGlowEffect, useGradientBorderEffect, useNoiseEffect, useSpotlightEffect };
package/dist/index.d.ts CHANGED
@@ -18,6 +18,21 @@ interface NoiseOptions extends EffectBaseOptions {
18
18
  size?: string | number;
19
19
  intensity?: number;
20
20
  }
21
+ interface SpotlightOptions extends EffectBaseOptions {
22
+ mode?: 'glow' | 'reveal';
23
+ size?: string | number;
24
+ intensity?: number;
25
+ color?: string;
26
+ softness?: number;
27
+ coreIntensity?: number;
28
+ x?: string | number;
29
+ y?: string | number;
30
+ followPointer?: boolean;
31
+ revealColor?: string;
32
+ revealImage?: string;
33
+ revealSize?: string | number;
34
+ revealOpacity?: number;
35
+ }
21
36
 
22
37
  declare function useGradientBorderEffect<T extends HTMLElement = HTMLElement>(options?: GradientBorderOptions): (next: T | null) => void;
23
38
 
@@ -25,4 +40,6 @@ declare function useGlowEffect<T extends HTMLElement = HTMLElement>(options?: Gl
25
40
 
26
41
  declare function useNoiseEffect<T extends HTMLElement = HTMLElement>(options?: NoiseOptions): (next: T | null) => void;
27
42
 
28
- export { type EffectBaseOptions, type GlowOptions, type GradientBorderOptions, type NoiseOptions, useGlowEffect, useGradientBorderEffect, useNoiseEffect };
43
+ declare function useSpotlightEffect<T extends HTMLElement = HTMLElement>(options?: SpotlightOptions): (next: T | null) => void;
44
+
45
+ export { type EffectBaseOptions, type GlowOptions, type GradientBorderOptions, type NoiseOptions, type SpotlightOptions, useGlowEffect, useGradientBorderEffect, useNoiseEffect, useSpotlightEffect };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import {toCssAngle,toCssLength,toCssNumber,ensureGlobalStyle,applyEffectClass,applyCssVars}from'@protohiro/effects-core';import {useMemo,useState,useEffect,useCallback}from'react';var t=`
1
+ import {toCssAngle,toCssLength,toCssNumber,ensureGlobalStyle,applyEffectClass,applyCssVars}from'@protohiro/effects-core';import {useMemo,useState,useEffect,useCallback}from'react';var a=`
2
2
  .pe-gradient-border {
3
3
  position: relative;
4
4
  isolation: isolate;
@@ -65,10 +65,109 @@ import {toCssAngle,toCssLength,toCssNumber,ensureGlobalStyle,applyEffectClass,ap
65
65
  pointer-events: none;
66
66
  }
67
67
 
68
+ .pe-spotlight {
69
+ position: relative;
70
+ isolation: isolate;
71
+ }
72
+
73
+ .pe-spotlight::before {
74
+ content: '';
75
+ position: absolute;
76
+ inset: 0;
77
+ border-radius: inherit;
78
+ pointer-events: none;
79
+ opacity: var(--pe-spotlight-intensity, 0.45);
80
+ background:
81
+ radial-gradient(
82
+ circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),
83
+ color-mix(
84
+ in srgb,
85
+ var(--pe-spotlight-color, #7dd3fc) calc(var(--pe-spotlight-core-intensity, 0.58) * 100%),
86
+ white
87
+ )
88
+ 0%,
89
+ color-mix(
90
+ in srgb,
91
+ var(--pe-spotlight-color, #7dd3fc) calc((0.48 + var(--pe-spotlight-core-intensity, 0.58) * 0.32) * 100%),
92
+ white
93
+ )
94
+ calc(var(--pe-spotlight-size, 180px) * 0.14),
95
+ color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 38%, transparent) calc(var(--pe-spotlight-size, 180px) * 0.4),
96
+ color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 20%, transparent) var(--pe-spotlight-size, 180px),
97
+ transparent calc(var(--pe-spotlight-size, 180px) * 1.45)
98
+ ),
99
+ radial-gradient(
100
+ circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),
101
+ color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 16%, transparent) 0%,
102
+ color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 8%, transparent) calc(var(--pe-spotlight-size, 180px) * 1.2),
103
+ transparent calc(var(--pe-spotlight-size, 180px) * 1.8)
104
+ );
105
+ mix-blend-mode: screen;
106
+ filter: blur(calc(1px + var(--pe-spotlight-softness, 0.72) * 6px)) saturate(128%);
107
+ transition: background 120ms ease-out, opacity 120ms ease-out;
108
+ }
109
+
110
+ @supports ((-webkit-mask-image: radial-gradient(white, black)) or (mask-image: radial-gradient(white, black))) {
111
+ .pe-spotlight.pe-spotlight--reveal::before {
112
+ opacity: var(--pe-spotlight-reveal-opacity, 0.92);
113
+ background-image:
114
+ var(
115
+ --pe-spotlight-reveal-image,
116
+ url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 220'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0%25' stop-color='%236d28d9'/%3E%3Cstop offset='52%25' stop-color='%230ea5e9'/%3E%3Cstop offset='100%25' stop-color='%2322d3ee'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='320' height='220' fill='url(%23g)'/%3E%3Ccircle cx='86' cy='74' r='52' fill='rgba(255,255,255,0.24)'/%3E%3Ccircle cx='252' cy='154' r='70' fill='rgba(255,255,255,0.14)'/%3E%3C/svg%3E")
117
+ ),
118
+ linear-gradient(var(--pe-spotlight-reveal-color, #38bdf8), var(--pe-spotlight-reveal-color, #38bdf8));
119
+ background-size: var(--pe-spotlight-reveal-size, cover), cover;
120
+ background-position: center;
121
+ background-repeat: no-repeat;
122
+ mix-blend-mode: normal;
123
+ filter: saturate(120%);
124
+ -webkit-mask-image: radial-gradient(
125
+ circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),
126
+ rgba(0, 0, 0, 1) 0,
127
+ rgba(0, 0, 0, 1) calc(var(--pe-spotlight-size, 180px) * 0.36),
128
+ rgba(0, 0, 0, 0.75) calc(var(--pe-spotlight-size, 180px) * 0.72),
129
+ transparent calc(var(--pe-spotlight-size, 180px) * 1.22)
130
+ );
131
+ mask-image: radial-gradient(
132
+ circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),
133
+ rgba(0, 0, 0, 1) 0,
134
+ rgba(0, 0, 0, 1) calc(var(--pe-spotlight-size, 180px) * 0.36),
135
+ rgba(0, 0, 0, 0.75) calc(var(--pe-spotlight-size, 180px) * 0.72),
136
+ transparent calc(var(--pe-spotlight-size, 180px) * 1.22)
137
+ );
138
+ }
139
+ }
140
+
141
+ @supports not (color: color-mix(in srgb, white, black)) {
142
+ .pe-spotlight::before {
143
+ background:
144
+ radial-gradient(
145
+ circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),
146
+ rgba(255, 255, 255, 0.86) 0%,
147
+ rgba(255, 255, 255, 0.56) calc(var(--pe-spotlight-size, 180px) * 0.14),
148
+ rgba(255, 255, 255, 0.3) calc(var(--pe-spotlight-size, 180px) * 0.4),
149
+ rgba(255, 255, 255, 0.16) var(--pe-spotlight-size, 180px),
150
+ transparent calc(var(--pe-spotlight-size, 180px) * 1.45)
151
+ ),
152
+ radial-gradient(
153
+ circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),
154
+ rgba(255, 255, 255, 0.16) 0%,
155
+ rgba(255, 255, 255, 0.08) calc(var(--pe-spotlight-size, 180px) * 1.2),
156
+ transparent calc(var(--pe-spotlight-size, 180px) * 1.8)
157
+ );
158
+ }
159
+
160
+ .pe-spotlight.pe-spotlight--reveal::before {
161
+ background-image:
162
+ var(--pe-spotlight-reveal-image, linear-gradient(135deg, rgba(109, 40, 217, 0.9), rgba(14, 165, 233, 0.95))),
163
+ linear-gradient(var(--pe-spotlight-reveal-color, #38bdf8), var(--pe-spotlight-reveal-color, #38bdf8));
164
+ }
165
+ }
166
+
68
167
  @media (prefers-reduced-motion: reduce) {
69
168
  .pe-noise::after {
70
169
  opacity: calc(var(--pe-noise-opacity, 0.28) * 0.9);
71
170
  }
72
171
  }
73
- `;function o(e){let[r,a]=useState(null);return useEffect(()=>{if(!r||e.disabled)return;ensureGlobalStyle(e.styleId,e.styleText);let s=[];return s.push(applyEffectClass(r,e.className)),e.userClassName&&s.push(applyEffectClass(r,e.userClassName)),s.push(applyCssVars(r,e.vars)),()=>{for(let d of s.reverse())d();}},[r,e.className,e.userClassName,e.disabled,e.styleId,e.styleText,e.vars]),useCallback(s=>{a(s);},[])}var x="protohiro-effects-base";function E(e={}){let r=useMemo(()=>({"--pe-gb-thickness":toCssLength(e.thickness),"--pe-gb-radius":toCssLength(e.radius),"--pe-gb-colors":e.colors,"--pe-gb-angle":toCssAngle(e.angle)}),[e.thickness,e.radius,e.colors,e.angle]);return o({className:"pe-gradient-border",userClassName:e.className,vars:r,disabled:e.disabled,styleId:x,styleText:t})}var T="protohiro-effects-base";function h(e={}){let r=useMemo(()=>({"--pe-glow-color":e.color,"--pe-glow-blur":toCssLength(e.blur),"--pe-glow-spread":toCssLength(e.spread),"--pe-glow-opacity":toCssNumber(e.opacity)}),[e.color,e.blur,e.spread,e.opacity]);return o({className:"pe-glow",userClassName:e.className,vars:r,disabled:e.disabled,styleId:T,styleText:t})}var k="protohiro-effects-base";function L(e){if(e!==void 0&&!Number.isNaN(e))return Math.max(0,Math.min(1,e))}function S(e={}){let r=L(e.intensity),a=useMemo(()=>({"--pe-noise-size":toCssLength(e.size??120),"--pe-noise-opacity":toCssNumber(r??.2)}),[e.size,r]);return o({className:"pe-noise",userClassName:e.className,vars:a,disabled:e.disabled,styleId:k,styleText:t})}export{h as useGlowEffect,E as useGradientBorderEffect,S as useNoiseEffect};//# sourceMappingURL=index.js.map
172
+ `;function i(e){let[t,o]=useState(null);return useEffect(()=>{if(!t||e.disabled)return;ensureGlobalStyle(e.styleId,e.styleText);let s=[];return s.push(applyEffectClass(t,e.className)),e.userClassName&&s.push(applyEffectClass(t,e.userClassName)),s.push(applyCssVars(t,e.vars)),()=>{for(let l of s.reverse())l();}},[t,e.className,e.userClassName,e.disabled,e.styleId,e.styleText,e.vars]),useCallback(s=>{o(s);},[])}var G="protohiro-effects-base";function F(e={}){let t=useMemo(()=>({"--pe-gb-thickness":toCssLength(e.thickness),"--pe-gb-radius":toCssLength(e.radius),"--pe-gb-colors":e.colors,"--pe-gb-angle":toCssAngle(e.angle)}),[e.thickness,e.radius,e.colors,e.angle]);return i({className:"pe-gradient-border",userClassName:e.className,vars:t,disabled:e.disabled,styleId:G,styleText:a})}var _="protohiro-effects-base";function B(e={}){let t=useMemo(()=>({"--pe-glow-color":e.color,"--pe-glow-blur":toCssLength(e.blur),"--pe-glow-spread":toCssLength(e.spread),"--pe-glow-opacity":toCssNumber(e.opacity)}),[e.color,e.blur,e.spread,e.opacity]);return i({className:"pe-glow",userClassName:e.className,vars:t,disabled:e.disabled,styleId:_,styleText:a})}var W="protohiro-effects-base";function $(e){if(e!==void 0&&!Number.isNaN(e))return Math.max(0,Math.min(1,e))}function A(e={}){let t=$(e.intensity),o=useMemo(()=>({"--pe-noise-size":toCssLength(e.size??120),"--pe-noise-opacity":toCssNumber(t??.2)}),[e.size,t]);return i({className:"pe-noise",userClassName:e.className,vars:o,disabled:e.disabled,styleId:W,styleText:a})}var J="protohiro-effects-base";function c(e){if(!(e===void 0||Number.isNaN(e)))return Math.max(0,Math.min(1,e))}function k(e){if(e!==void 0)return typeof e=="number"?`${e}%`:e}function K(e){if(!e)return;let t=e.trim();if(t)return t.startsWith("url(")||t.startsWith("linear-gradient(")||t.startsWith("radial-gradient(")||t.startsWith("conic-gradient(")?t:`url("${t.replace(/"/g,'\\"')}")`}function Q(e){return e===void 0||typeof e!="number"?e:Math.max(0,e)}function Z(e={}){let[t,o]=useState(null),s=e.mode==="reveal",l=Q(e.size),g=c(e.intensity),f=c(e.softness),m=c(e.coreIntensity),u=c(e.revealOpacity),b=k(e.x),h=k(e.y),v=K(e.revealImage),x=useMemo(()=>({"--pe-spotlight-size":toCssLength(l),"--pe-spotlight-intensity":toCssNumber(g),"--pe-spotlight-color":e.color,"--pe-spotlight-softness":toCssNumber(f),"--pe-spotlight-core-intensity":toCssNumber(m),"--pe-spotlight-x":b,"--pe-spotlight-y":h,"--pe-spotlight-reveal-color":e.revealColor,"--pe-spotlight-reveal-image":v,"--pe-spotlight-reveal-size":toCssLength(e.revealSize),"--pe-spotlight-reveal-opacity":toCssNumber(u)}),[l,g,f,m,e.color,b,h,e.revealColor,v,e.revealSize,u]);return useEffect(()=>{if(!t||e.disabled)return;ensureGlobalStyle(J,a);let r=[];return r.push(applyEffectClass(t,"pe-spotlight")),s&&r.push(applyEffectClass(t,"pe-spotlight--reveal")),e.className&&r.push(applyEffectClass(t,e.className)),r.push(applyCssVars(t,x)),()=>{for(let n of r.reverse())n();}},[t,e.disabled,e.className,s,x]),useEffect(()=>{if(!t||e.disabled||!e.followPointer)return;let r=n=>{t.style.setProperty("--pe-spotlight-x",`${n.offsetX}px`),t.style.setProperty("--pe-spotlight-y",`${n.offsetY}px`);};return t.addEventListener("pointermove",r),()=>{t.removeEventListener("pointermove",r);}},[t,e.disabled,e.followPointer]),useCallback(r=>{o(r);},[])}export{B as useGlowEffect,F as useGradientBorderEffect,A as useNoiseEffect,Z as useSpotlightEffect};//# sourceMappingURL=index.js.map
74
173
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/shared/effectStyles.ts","../src/shared/useCssEffect.ts","../src/effects/useGradientBorderEffect.ts","../src/effects/useGlowEffect.ts","../src/effects/useNoiseEffect.ts"],"names":["EFFECT_STYLES","useCssEffect","input","node","setNode","useState","useEffect","ensureGlobalStyle","cleanups","applyEffectClass","applyCssVars","cleanup","useCallback","next","STYLE_ID","useGradientBorderEffect","options","vars","useMemo","toCssLength","toCssAngle","useGlowEffect","toCssNumber","clampUnit","value","useNoiseEffect","intensity"],"mappings":"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","file":"index.js","sourcesContent":["export const EFFECT_STYLES = `\n.pe-gradient-border {\n position: relative;\n isolation: isolate;\n border-radius: var(--pe-gb-radius, inherit);\n border-color: transparent;\n}\n\n.pe-gradient-border::before {\n content: '';\n position: absolute;\n z-index: -1;\n inset: calc(var(--pe-gb-thickness, 2px) * -1);\n border-radius: calc(var(--pe-gb-radius, 0px) + var(--pe-gb-thickness, 2px));\n box-sizing: border-box;\n padding: var(--pe-gb-thickness, 2px);\n background: linear-gradient(var(--pe-gb-angle, 120deg), var(--pe-gb-colors, #5eead4, #0ea5e9));\n pointer-events: none;\n\n /* Hollow out the center so only the ring remains. */\n -webkit-mask:\n linear-gradient(#fff 0 0) content-box,\n linear-gradient(#fff 0 0);\n -webkit-mask-composite: xor;\n mask:\n linear-gradient(#fff 0 0) content-box,\n linear-gradient(#fff 0 0);\n mask-composite: exclude;\n}\n\n@supports not ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {\n .pe-gradient-border::before {\n z-index: -1;\n inset: calc(var(--pe-gb-thickness, 2px) * -1);\n border-radius: calc(var(--pe-gb-radius, 0px) + var(--pe-gb-thickness, 2px));\n padding: 0;\n border: var(--pe-gb-thickness, 2px) solid;\n border-image: linear-gradient(var(--pe-gb-angle, 120deg), var(--pe-gb-colors, #5eead4, #0ea5e9)) 1;\n }\n}\n\n.pe-glow {\n box-shadow:\n 0 0 var(--pe-glow-blur, 24px)\n color-mix(in srgb, var(--pe-glow-color, #38bdf8) calc(var(--pe-glow-opacity, 0.4) * 100%), transparent),\n 0 0 var(--pe-glow-spread, 2px)\n color-mix(in srgb, var(--pe-glow-color, #38bdf8) calc(var(--pe-glow-opacity, 0.2) * 100%), transparent);\n}\n\n.pe-noise {\n position: relative;\n}\n\n.pe-noise::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160 160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.95' numOctaves='2' seed='13' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E\");\n background-size: var(--pe-noise-size, 80px) var(--pe-noise-size, 80px);\n background-repeat: repeat;\n image-rendering: pixelated;\n filter: contrast(190%) brightness(110%);\n opacity: var(--pe-noise-opacity, 0.28);\n pointer-events: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .pe-noise::after {\n opacity: calc(var(--pe-noise-opacity, 0.28) * 0.9);\n }\n}\n`;\n","import { applyCssVars, applyEffectClass, ensureGlobalStyle } from '@protohiro/effects-core';\nimport { useCallback, useEffect, useState } from 'react';\n\ntype UseCssEffectInput = {\n className: string;\n userClassName?: string;\n vars: Record<string, string | undefined>;\n disabled?: boolean;\n styleId: string;\n styleText: string;\n};\n\nexport function useCssEffect<T extends HTMLElement>(input: UseCssEffectInput) {\n const [node, setNode] = useState<T | null>(null);\n\n useEffect(() => {\n if (!node || input.disabled) {\n return;\n }\n\n ensureGlobalStyle(input.styleId, input.styleText);\n\n const cleanups: Array<() => void> = [];\n cleanups.push(applyEffectClass(node, input.className));\n\n if (input.userClassName) {\n cleanups.push(applyEffectClass(node, input.userClassName));\n }\n\n cleanups.push(applyCssVars(node, input.vars));\n\n return () => {\n for (const cleanup of cleanups.reverse()) {\n cleanup();\n }\n };\n }, [\n node,\n input.className,\n input.userClassName,\n input.disabled,\n input.styleId,\n input.styleText,\n input.vars,\n ]);\n\n return useCallback((next: T | null) => {\n setNode(next);\n }, []);\n}\n","import { toCssAngle, toCssLength } from '@protohiro/effects-core';\nimport { useMemo } from 'react';\n\nimport type { GradientBorderOptions } from '../types';\nimport { EFFECT_STYLES } from '../shared/effectStyles';\nimport { useCssEffect } from '../shared/useCssEffect';\n\nconst STYLE_ID = 'protohiro-effects-base';\n\nexport function useGradientBorderEffect<T extends HTMLElement = HTMLElement>(\n options: GradientBorderOptions = {},\n) {\n const vars = useMemo(\n () => ({\n '--pe-gb-thickness': toCssLength(options.thickness),\n '--pe-gb-radius': toCssLength(options.radius),\n '--pe-gb-colors': options.colors,\n '--pe-gb-angle': toCssAngle(options.angle),\n }),\n [options.thickness, options.radius, options.colors, options.angle],\n );\n\n return useCssEffect<T>({\n className: 'pe-gradient-border',\n userClassName: options.className,\n vars,\n disabled: options.disabled,\n styleId: STYLE_ID,\n styleText: EFFECT_STYLES,\n });\n}\n","import { toCssLength, toCssNumber } from '@protohiro/effects-core';\nimport { useMemo } from 'react';\n\nimport type { GlowOptions } from '../types';\nimport { EFFECT_STYLES } from '../shared/effectStyles';\nimport { useCssEffect } from '../shared/useCssEffect';\n\nconst STYLE_ID = 'protohiro-effects-base';\n\nexport function useGlowEffect<T extends HTMLElement = HTMLElement>(options: GlowOptions = {}) {\n const vars = useMemo(\n () => ({\n '--pe-glow-color': options.color,\n '--pe-glow-blur': toCssLength(options.blur),\n '--pe-glow-spread': toCssLength(options.spread),\n '--pe-glow-opacity': toCssNumber(options.opacity),\n }),\n [options.color, options.blur, options.spread, options.opacity],\n );\n\n return useCssEffect<T>({\n className: 'pe-glow',\n userClassName: options.className,\n vars,\n disabled: options.disabled,\n styleId: STYLE_ID,\n styleText: EFFECT_STYLES,\n });\n}\n","import { toCssLength, toCssNumber } from '@protohiro/effects-core';\nimport { useMemo } from 'react';\n\nimport type { NoiseOptions } from '../types';\nimport { EFFECT_STYLES } from '../shared/effectStyles';\nimport { useCssEffect } from '../shared/useCssEffect';\n\nconst STYLE_ID = 'protohiro-effects-base';\n\nfunction clampUnit(value: number | undefined): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (Number.isNaN(value)) {\n return undefined;\n }\n\n return Math.max(0, Math.min(1, value));\n}\n\nexport function useNoiseEffect<T extends HTMLElement = HTMLElement>(options: NoiseOptions = {}) {\n const intensity = clampUnit(options.intensity);\n\n const vars = useMemo(\n () => ({\n '--pe-noise-size': toCssLength(options.size ?? 120),\n '--pe-noise-opacity': toCssNumber(intensity ?? 0.2),\n }),\n [options.size, intensity],\n );\n\n return useCssEffect<T>({\n className: 'pe-noise',\n userClassName: options.className,\n vars,\n disabled: options.disabled,\n styleId: STYLE_ID,\n styleText: EFFECT_STYLES,\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/shared/effectStyles.ts","../src/shared/useCssEffect.ts","../src/effects/useGradientBorderEffect.ts","../src/effects/useGlowEffect.ts","../src/effects/useNoiseEffect.ts","../src/effects/useSpotlightEffect.ts"],"names":["EFFECT_STYLES","useCssEffect","input","node","setNode","useState","useEffect","ensureGlobalStyle","cleanups","applyEffectClass","applyCssVars","cleanup","useCallback","next","STYLE_ID","useGradientBorderEffect","options","vars","useMemo","toCssLength","toCssAngle","useGlowEffect","toCssNumber","clampUnit","value","useNoiseEffect","intensity","toCssPosition","toCssImage","trimmed","normalizeSpotlightSize","useSpotlightEffect","isRevealMode","size","softness","coreIntensity","revealOpacity","x","y","revealImage","onPointerMove","event"],"mappings":"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","file":"index.js","sourcesContent":["export const EFFECT_STYLES = `\n.pe-gradient-border {\n position: relative;\n isolation: isolate;\n border-radius: var(--pe-gb-radius, inherit);\n border-color: transparent;\n}\n\n.pe-gradient-border::before {\n content: '';\n position: absolute;\n z-index: -1;\n inset: calc(var(--pe-gb-thickness, 2px) * -1);\n border-radius: calc(var(--pe-gb-radius, 0px) + var(--pe-gb-thickness, 2px));\n box-sizing: border-box;\n padding: var(--pe-gb-thickness, 2px);\n background: linear-gradient(var(--pe-gb-angle, 120deg), var(--pe-gb-colors, #5eead4, #0ea5e9));\n pointer-events: none;\n\n /* Hollow out the center so only the ring remains. */\n -webkit-mask:\n linear-gradient(#fff 0 0) content-box,\n linear-gradient(#fff 0 0);\n -webkit-mask-composite: xor;\n mask:\n linear-gradient(#fff 0 0) content-box,\n linear-gradient(#fff 0 0);\n mask-composite: exclude;\n}\n\n@supports not ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {\n .pe-gradient-border::before {\n z-index: -1;\n inset: calc(var(--pe-gb-thickness, 2px) * -1);\n border-radius: calc(var(--pe-gb-radius, 0px) + var(--pe-gb-thickness, 2px));\n padding: 0;\n border: var(--pe-gb-thickness, 2px) solid;\n border-image: linear-gradient(var(--pe-gb-angle, 120deg), var(--pe-gb-colors, #5eead4, #0ea5e9)) 1;\n }\n}\n\n.pe-glow {\n box-shadow:\n 0 0 var(--pe-glow-blur, 24px)\n color-mix(in srgb, var(--pe-glow-color, #38bdf8) calc(var(--pe-glow-opacity, 0.4) * 100%), transparent),\n 0 0 var(--pe-glow-spread, 2px)\n color-mix(in srgb, var(--pe-glow-color, #38bdf8) calc(var(--pe-glow-opacity, 0.2) * 100%), transparent);\n}\n\n.pe-noise {\n position: relative;\n}\n\n.pe-noise::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160 160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.95' numOctaves='2' seed='13' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E\");\n background-size: var(--pe-noise-size, 80px) var(--pe-noise-size, 80px);\n background-repeat: repeat;\n image-rendering: pixelated;\n filter: contrast(190%) brightness(110%);\n opacity: var(--pe-noise-opacity, 0.28);\n pointer-events: none;\n}\n\n.pe-spotlight {\n position: relative;\n isolation: isolate;\n}\n\n.pe-spotlight::before {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n opacity: var(--pe-spotlight-intensity, 0.45);\n background:\n radial-gradient(\n circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),\n color-mix(\n in srgb,\n var(--pe-spotlight-color, #7dd3fc) calc(var(--pe-spotlight-core-intensity, 0.58) * 100%),\n white\n )\n 0%,\n color-mix(\n in srgb,\n var(--pe-spotlight-color, #7dd3fc) calc((0.48 + var(--pe-spotlight-core-intensity, 0.58) * 0.32) * 100%),\n white\n )\n calc(var(--pe-spotlight-size, 180px) * 0.14),\n color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 38%, transparent) calc(var(--pe-spotlight-size, 180px) * 0.4),\n color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 20%, transparent) var(--pe-spotlight-size, 180px),\n transparent calc(var(--pe-spotlight-size, 180px) * 1.45)\n ),\n radial-gradient(\n circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),\n color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 16%, transparent) 0%,\n color-mix(in srgb, var(--pe-spotlight-color, #7dd3fc) 8%, transparent) calc(var(--pe-spotlight-size, 180px) * 1.2),\n transparent calc(var(--pe-spotlight-size, 180px) * 1.8)\n );\n mix-blend-mode: screen;\n filter: blur(calc(1px + var(--pe-spotlight-softness, 0.72) * 6px)) saturate(128%);\n transition: background 120ms ease-out, opacity 120ms ease-out;\n}\n\n@supports ((-webkit-mask-image: radial-gradient(white, black)) or (mask-image: radial-gradient(white, black))) {\n .pe-spotlight.pe-spotlight--reveal::before {\n opacity: var(--pe-spotlight-reveal-opacity, 0.92);\n background-image:\n var(\n --pe-spotlight-reveal-image,\n url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 220'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0%25' stop-color='%236d28d9'/%3E%3Cstop offset='52%25' stop-color='%230ea5e9'/%3E%3Cstop offset='100%25' stop-color='%2322d3ee'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='320' height='220' fill='url(%23g)'/%3E%3Ccircle cx='86' cy='74' r='52' fill='rgba(255,255,255,0.24)'/%3E%3Ccircle cx='252' cy='154' r='70' fill='rgba(255,255,255,0.14)'/%3E%3C/svg%3E\")\n ),\n linear-gradient(var(--pe-spotlight-reveal-color, #38bdf8), var(--pe-spotlight-reveal-color, #38bdf8));\n background-size: var(--pe-spotlight-reveal-size, cover), cover;\n background-position: center;\n background-repeat: no-repeat;\n mix-blend-mode: normal;\n filter: saturate(120%);\n -webkit-mask-image: radial-gradient(\n circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),\n rgba(0, 0, 0, 1) 0,\n rgba(0, 0, 0, 1) calc(var(--pe-spotlight-size, 180px) * 0.36),\n rgba(0, 0, 0, 0.75) calc(var(--pe-spotlight-size, 180px) * 0.72),\n transparent calc(var(--pe-spotlight-size, 180px) * 1.22)\n );\n mask-image: radial-gradient(\n circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),\n rgba(0, 0, 0, 1) 0,\n rgba(0, 0, 0, 1) calc(var(--pe-spotlight-size, 180px) * 0.36),\n rgba(0, 0, 0, 0.75) calc(var(--pe-spotlight-size, 180px) * 0.72),\n transparent calc(var(--pe-spotlight-size, 180px) * 1.22)\n );\n }\n}\n\n@supports not (color: color-mix(in srgb, white, black)) {\n .pe-spotlight::before {\n background:\n radial-gradient(\n circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),\n rgba(255, 255, 255, 0.86) 0%,\n rgba(255, 255, 255, 0.56) calc(var(--pe-spotlight-size, 180px) * 0.14),\n rgba(255, 255, 255, 0.3) calc(var(--pe-spotlight-size, 180px) * 0.4),\n rgba(255, 255, 255, 0.16) var(--pe-spotlight-size, 180px),\n transparent calc(var(--pe-spotlight-size, 180px) * 1.45)\n ),\n radial-gradient(\n circle at var(--pe-spotlight-x, 50%) var(--pe-spotlight-y, 50%),\n rgba(255, 255, 255, 0.16) 0%,\n rgba(255, 255, 255, 0.08) calc(var(--pe-spotlight-size, 180px) * 1.2),\n transparent calc(var(--pe-spotlight-size, 180px) * 1.8)\n );\n }\n\n .pe-spotlight.pe-spotlight--reveal::before {\n background-image:\n var(--pe-spotlight-reveal-image, linear-gradient(135deg, rgba(109, 40, 217, 0.9), rgba(14, 165, 233, 0.95))),\n linear-gradient(var(--pe-spotlight-reveal-color, #38bdf8), var(--pe-spotlight-reveal-color, #38bdf8));\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .pe-noise::after {\n opacity: calc(var(--pe-noise-opacity, 0.28) * 0.9);\n }\n}\n`;\n","import { applyCssVars, applyEffectClass, ensureGlobalStyle } from '@protohiro/effects-core';\nimport { useCallback, useEffect, useState } from 'react';\n\ntype UseCssEffectInput = {\n className: string;\n userClassName?: string;\n vars: Record<string, string | undefined>;\n disabled?: boolean;\n styleId: string;\n styleText: string;\n};\n\nexport function useCssEffect<T extends HTMLElement>(input: UseCssEffectInput) {\n const [node, setNode] = useState<T | null>(null);\n\n useEffect(() => {\n if (!node || input.disabled) {\n return;\n }\n\n ensureGlobalStyle(input.styleId, input.styleText);\n\n const cleanups: Array<() => void> = [];\n cleanups.push(applyEffectClass(node, input.className));\n\n if (input.userClassName) {\n cleanups.push(applyEffectClass(node, input.userClassName));\n }\n\n cleanups.push(applyCssVars(node, input.vars));\n\n return () => {\n for (const cleanup of cleanups.reverse()) {\n cleanup();\n }\n };\n }, [\n node,\n input.className,\n input.userClassName,\n input.disabled,\n input.styleId,\n input.styleText,\n input.vars,\n ]);\n\n return useCallback((next: T | null) => {\n setNode(next);\n }, []);\n}\n","import { toCssAngle, toCssLength } from '@protohiro/effects-core';\nimport { useMemo } from 'react';\n\nimport type { GradientBorderOptions } from '../types';\nimport { EFFECT_STYLES } from '../shared/effectStyles';\nimport { useCssEffect } from '../shared/useCssEffect';\n\nconst STYLE_ID = 'protohiro-effects-base';\n\nexport function useGradientBorderEffect<T extends HTMLElement = HTMLElement>(\n options: GradientBorderOptions = {},\n) {\n const vars = useMemo(\n () => ({\n '--pe-gb-thickness': toCssLength(options.thickness),\n '--pe-gb-radius': toCssLength(options.radius),\n '--pe-gb-colors': options.colors,\n '--pe-gb-angle': toCssAngle(options.angle),\n }),\n [options.thickness, options.radius, options.colors, options.angle],\n );\n\n return useCssEffect<T>({\n className: 'pe-gradient-border',\n userClassName: options.className,\n vars,\n disabled: options.disabled,\n styleId: STYLE_ID,\n styleText: EFFECT_STYLES,\n });\n}\n","import { toCssLength, toCssNumber } from '@protohiro/effects-core';\nimport { useMemo } from 'react';\n\nimport type { GlowOptions } from '../types';\nimport { EFFECT_STYLES } from '../shared/effectStyles';\nimport { useCssEffect } from '../shared/useCssEffect';\n\nconst STYLE_ID = 'protohiro-effects-base';\n\nexport function useGlowEffect<T extends HTMLElement = HTMLElement>(options: GlowOptions = {}) {\n const vars = useMemo(\n () => ({\n '--pe-glow-color': options.color,\n '--pe-glow-blur': toCssLength(options.blur),\n '--pe-glow-spread': toCssLength(options.spread),\n '--pe-glow-opacity': toCssNumber(options.opacity),\n }),\n [options.color, options.blur, options.spread, options.opacity],\n );\n\n return useCssEffect<T>({\n className: 'pe-glow',\n userClassName: options.className,\n vars,\n disabled: options.disabled,\n styleId: STYLE_ID,\n styleText: EFFECT_STYLES,\n });\n}\n","import { toCssLength, toCssNumber } from '@protohiro/effects-core';\nimport { useMemo } from 'react';\n\nimport type { NoiseOptions } from '../types';\nimport { EFFECT_STYLES } from '../shared/effectStyles';\nimport { useCssEffect } from '../shared/useCssEffect';\n\nconst STYLE_ID = 'protohiro-effects-base';\n\nfunction clampUnit(value: number | undefined): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (Number.isNaN(value)) {\n return undefined;\n }\n\n return Math.max(0, Math.min(1, value));\n}\n\nexport function useNoiseEffect<T extends HTMLElement = HTMLElement>(options: NoiseOptions = {}) {\n const intensity = clampUnit(options.intensity);\n\n const vars = useMemo(\n () => ({\n '--pe-noise-size': toCssLength(options.size ?? 120),\n '--pe-noise-opacity': toCssNumber(intensity ?? 0.2),\n }),\n [options.size, intensity],\n );\n\n return useCssEffect<T>({\n className: 'pe-noise',\n userClassName: options.className,\n vars,\n disabled: options.disabled,\n styleId: STYLE_ID,\n styleText: EFFECT_STYLES,\n });\n}\n","import {\n applyCssVars,\n applyEffectClass,\n ensureGlobalStyle,\n toCssLength,\n toCssNumber,\n} from '@protohiro/effects-core';\nimport { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport type { SpotlightOptions } from '../types';\nimport { EFFECT_STYLES } from '../shared/effectStyles';\n\nconst STYLE_ID = 'protohiro-effects-base';\n\nfunction clampUnit(value: number | undefined): number | undefined {\n if (value === undefined || Number.isNaN(value)) {\n return undefined;\n }\n\n return Math.max(0, Math.min(1, value));\n}\n\nfunction toCssPosition(value: string | number | undefined): string | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n return typeof value === 'number' ? `${value}%` : value;\n}\n\nfunction toCssImage(value: string | undefined): string | undefined {\n if (!value) {\n return undefined;\n }\n\n const trimmed = value.trim();\n if (!trimmed) {\n return undefined;\n }\n\n if (\n trimmed.startsWith('url(') ||\n trimmed.startsWith('linear-gradient(') ||\n trimmed.startsWith('radial-gradient(') ||\n trimmed.startsWith('conic-gradient(')\n ) {\n return trimmed;\n }\n\n return `url(\"${trimmed.replace(/\"/g, '\\\\\"')}\")`;\n}\n\nfunction normalizeSpotlightSize(\n value: string | number | undefined,\n): string | number | undefined {\n if (value === undefined || typeof value !== 'number') {\n return value;\n }\n\n return Math.max(0, value);\n}\n\nexport function useSpotlightEffect<T extends HTMLElement = HTMLElement>(\n options: SpotlightOptions = {},\n) {\n const [node, setNode] = useState<T | null>(null);\n const isRevealMode = options.mode === 'reveal';\n const size = normalizeSpotlightSize(options.size);\n const intensity = clampUnit(options.intensity);\n const softness = clampUnit(options.softness);\n const coreIntensity = clampUnit(options.coreIntensity);\n const revealOpacity = clampUnit(options.revealOpacity);\n const x = toCssPosition(options.x);\n const y = toCssPosition(options.y);\n const revealImage = toCssImage(options.revealImage);\n\n const vars = useMemo(\n () => ({\n '--pe-spotlight-size': toCssLength(size),\n '--pe-spotlight-intensity': toCssNumber(intensity),\n '--pe-spotlight-color': options.color,\n '--pe-spotlight-softness': toCssNumber(softness),\n '--pe-spotlight-core-intensity': toCssNumber(coreIntensity),\n '--pe-spotlight-x': x,\n '--pe-spotlight-y': y,\n '--pe-spotlight-reveal-color': options.revealColor,\n '--pe-spotlight-reveal-image': revealImage,\n '--pe-spotlight-reveal-size': toCssLength(options.revealSize),\n '--pe-spotlight-reveal-opacity': toCssNumber(revealOpacity),\n }),\n [\n size,\n intensity,\n softness,\n coreIntensity,\n options.color,\n x,\n y,\n options.revealColor,\n revealImage,\n options.revealSize,\n revealOpacity,\n ],\n );\n\n useEffect(() => {\n if (!node || options.disabled) {\n return;\n }\n\n ensureGlobalStyle(STYLE_ID, EFFECT_STYLES);\n\n const cleanups: Array<() => void> = [];\n cleanups.push(applyEffectClass(node, 'pe-spotlight'));\n if (isRevealMode) {\n cleanups.push(applyEffectClass(node, 'pe-spotlight--reveal'));\n }\n\n if (options.className) {\n cleanups.push(applyEffectClass(node, options.className));\n }\n\n cleanups.push(applyCssVars(node, vars));\n\n return () => {\n for (const cleanup of cleanups.reverse()) {\n cleanup();\n }\n };\n }, [node, options.disabled, options.className, isRevealMode, vars]);\n\n useEffect(() => {\n if (!node || options.disabled || !options.followPointer) {\n return;\n }\n\n const onPointerMove = (event: PointerEvent) => {\n node.style.setProperty('--pe-spotlight-x', `${event.offsetX}px`);\n node.style.setProperty('--pe-spotlight-y', `${event.offsetY}px`);\n };\n\n node.addEventListener('pointermove', onPointerMove);\n\n return () => {\n node.removeEventListener('pointermove', onPointerMove);\n };\n }, [node, options.disabled, options.followPointer]);\n\n return useCallback((next: T | null) => {\n setNode(next);\n }, []);\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protohiro/effects",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Zero-wrapper React hooks for composable CSS effects.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -18,10 +18,19 @@
18
18
  },
19
19
  "keywords": [
20
20
  "protoeffects",
21
+ "protohiro",
21
22
  "react",
22
23
  "hooks",
23
24
  "css",
24
- "effects"
25
+ "effects",
26
+ "react-hooks",
27
+ "css-effects",
28
+ "ui-effects",
29
+ "gradient-border",
30
+ "glow",
31
+ "spotlight",
32
+ "noise-overlay",
33
+ "design-system"
25
34
  ],
26
35
  "publishConfig": {
27
36
  "access": "public"
@@ -39,7 +48,7 @@
39
48
  ],
40
49
  "sideEffects": false,
41
50
  "dependencies": {
42
- "@protohiro/effects-core": "0.1.2"
51
+ "@protohiro/effects-core": "0.1.3"
43
52
  },
44
53
  "peerDependencies": {
45
54
  "react": "^18.0.0",