@marcosdemik/liquidglass 1.1.1 → 2.0.1

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
@@ -4,7 +4,7 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dm/@marcosdemik/liquidglass.svg)](https://www.npmjs.com/package/@marcosdemik/liquidglass)
5
5
  [![license](https://img.shields.io/npm/l/@marcosdemik/liquidglass.svg)](https://github.com/MarcosDemik/liquidglass/blob/main/LICENSE)
6
6
 
7
- A React component that creates a **Liquid Glass** UI effect - glassmorphism with real-time refraction, chromatic aberration, and smooth GSAP animations.
7
+ A React component that creates a **Liquid Glass** UI effect with real-time refraction, specular highlights, and smooth GSAP animations.
8
8
 
9
9
  Built with SVG filters and WebGL displacement maps.
10
10
 
@@ -32,7 +32,7 @@ import { LiquidGlassButton } from "@marcosdemik/liquidglass";
32
32
 
33
33
  function App() {
34
34
  return (
35
- <LiquidGlassButton width={320} height={60} radius={60} chroma={3}>
35
+ <LiquidGlassButton width={320} height={60} radius={60}>
36
36
  Click me
37
37
  </LiquidGlassButton>
38
38
  );
@@ -58,30 +58,28 @@ The component includes a `"use client"` directive, so it works out of the box wi
58
58
  | `width` | `number` | `300` | Button width in pixels |
59
59
  | `height` | `number` | `56` | Button height in pixels |
60
60
  | `radius` | `number` | `60` | Border radius in pixels |
61
- | `glassColor` | `string` | `"rgba(255,255,255,0.05)"` | Background tint color of the glass |
61
+ | `glassColor` | `string` | `"transparent"` | Background tint color of the glass |
62
62
 
63
63
  ### Glass Effect
64
64
 
65
65
  | Prop | Type | Default | Description |
66
66
  |------|------|---------|-------------|
67
- | `displacement` | `number` | `35` | How much the background refracts (feDisplacementMap scale) |
68
- | `blur` | `number` | `2` | Gaussian blur applied to the background |
69
- | `chroma` | `number` | `3` | Chromatic aberration strength (RGB channel offset) |
70
- | `saturation` | `number` | `1.2` | Saturation boost on the final result (1 = normal) |
71
- | `distortion` | `number` | `15` | Normal map distortion scale in the shader |
72
- | `intensity` | `number` | `1.0` | Refraction intensity at the glass edge |
73
- | `edgeSize` | `number` | `40` | Thickness of the glass edge refraction zone |
74
- | `smoothness` | `number` | `1.0` | Blur on the displacement map (softens transitions) |
67
+ | `displacement` | `number` | `55` | How much the background refracts (feDisplacementMap scale) |
68
+ | `blur` | `number` | `1` | Gaussian blur applied to the background |
69
+ | `saturation` | `number` | `150` | Color saturation of the refracted result (feColorMatrix saturate value) |
70
+ | `brightness` | `number` | `1.1` | Brightness boost on the backdrop-filter (1 = normal) |
71
+ | `intensity` | `number` | `0.7` | Refraction intensity at the glass edge (0-1) |
72
+ | `edgeSize` | `number` | `30` | Thickness of the glass edge refraction zone in pixels |
73
+ | `specularWidth` | `number` | `0.02` | Specular rim thickness relative to the smallest dimension (0-1) |
75
74
 
76
75
  ### Hover Animation
77
76
 
78
77
  | Prop | Type | Default | Description |
79
78
  |------|------|---------|-------------|
80
- | `hoverScale` | `number` | `1.05` | Scale multiplier on hover |
81
- | `hoverDisplacement` | `number` | `65` | Displacement scale on hover |
79
+ | `hoverScale` | `number` | `1.08` | Scale multiplier on hover |
80
+ | `hoverDisplacement` | `number` | `125` | Displacement scale on hover |
82
81
  | `hoverBlur` | `number` | `4` | Blur amount on hover |
83
- | `hoverChromaMultiplier` | `number` | `2.5` | Multiplier applied to `chroma` on hover |
84
- | `hoverDuration` | `number` | `0.4` | Duration of hover animation in seconds |
82
+ | `hoverDuration` | `number` | `0.25` | Duration of hover animation in seconds |
85
83
  | `disableAnimation` | `boolean` | `false` | Disable all GSAP animations |
86
84
 
87
85
  ### Standard HTML
@@ -112,17 +110,17 @@ All standard `<button>` HTML attributes (`onClick`, `disabled`, `aria-label`, et
112
110
  </LiquidGlassButton>
113
111
  ```
114
112
 
115
- ### High Distortion
113
+ ### High Refraction
116
114
 
117
115
  ```tsx
118
116
  <LiquidGlassButton
119
117
  width={320}
120
118
  height={60}
121
119
  radius={60}
122
- displacement={60}
123
- distortion={30}
124
- chroma={8}
125
- intensity={2}
120
+ displacement={100}
121
+ intensity={1}
122
+ edgeSize={50}
123
+ saturation={200}
126
124
  >
127
125
  Distorted
128
126
  </LiquidGlassButton>
@@ -135,11 +133,11 @@ All standard `<button>` HTML attributes (`onClick`, `disabled`, `aria-label`, et
135
133
  width={320}
136
134
  height={60}
137
135
  radius={60}
138
- displacement={15}
136
+ displacement={25}
139
137
  blur={1}
140
- distortion={5}
141
- chroma={1}
142
- smoothness={3}
138
+ intensity={0.4}
139
+ edgeSize={15}
140
+ saturation={120}
143
141
  >
144
142
  Subtle
145
143
  </LiquidGlassButton>
@@ -165,11 +163,10 @@ All standard `<button>` HTML attributes (`onClick`, `disabled`, `aria-label`, et
165
163
  width={320}
166
164
  height={60}
167
165
  radius={60}
168
- hoverScale={1.1}
169
- hoverDisplacement={100}
166
+ hoverScale={1.12}
167
+ hoverDisplacement={150}
170
168
  hoverBlur={6}
171
- hoverChromaMultiplier={4}
172
- hoverDuration={0.6}
169
+ hoverDuration={0.4}
173
170
  >
174
171
  Strong Hover
175
172
  </LiquidGlassButton>
@@ -192,11 +189,27 @@ All standard `<button>` HTML attributes (`onClick`, `disabled`, `aria-label`, et
192
189
 
193
190
  The effect is built from three layers:
194
191
 
195
- 1. **WebGL Displacement Map** - A GLSL fragment shader computes a displacement map from a signed distance field (SDF) of a rounded rectangle. The shader runs on an offscreen canvas and outputs a PNG data URL. The WebGL context is cached as a singleton for performance.
192
+ 1. **WebGL Displacement + Specular Maps** - A GLSL fragment shader computes a displacement map and a specular highlight map from a signed distance field (SDF) of a rounded rectangle. 3D surface normals are derived from the SDF to create realistic light refraction at the edges. Both maps are rendered on an offscreen canvas and output as PNG data URLs. The WebGL context is cached as a singleton.
196
193
 
197
- 2. **SVG Filter Chain** - The displacement map feeds into an SVG `<filter>` that applies per-channel (R/G/B) `feDisplacementMap` at slightly different scales, producing chromatic aberration. Channels are recombined with `feBlend mode="screen"`.
194
+ 2. **SVG Filter Chain** - The displacement map feeds into an SVG `<filter>` with `feDisplacementMap` for background refraction, `feColorMatrix` for saturation control, and `feBlend` to composite the specular highlight layer on top. A `brightness()` function in the backdrop-filter adds a subtle glow.
198
195
 
199
- 3. **GSAP Animations** - Pointer events drive GSAP tweens that animate displacement scale, blur, chromatic separation, and button scale. Filter attributes are mutated directly each frame for maximum performance.
196
+ 3. **GSAP Animations** - Pointer events drive GSAP tweens that animate displacement scale, blur, and button scale. Filter attributes are mutated directly each frame for maximum performance.
197
+
198
+ ## Migration from v1.x
199
+
200
+ v2.0.0 replaced the chromatic aberration pipeline with a specular highlight pipeline for a more realistic glass effect. The following props were removed:
201
+
202
+ - `chroma` - removed (no more chromatic aberration)
203
+ - `distortion` - removed (shader normals are now computed from the SDF)
204
+ - `smoothness` - removed (displacement map blur is no longer needed)
205
+ - `hoverChromaMultiplier` - removed
206
+
207
+ New props added:
208
+
209
+ - `brightness` - controls backdrop brightness (default `1.1`)
210
+ - `specularWidth` - controls the specular rim thickness (default `0.02`)
211
+
212
+ Changed defaults: `displacement` (35 -> 55), `blur` (2 -> 1), `saturation` (1.2 -> 150), `hoverScale` (1.05 -> 1.08), `hoverDisplacement` (65 -> 125), `hoverDuration` (0.4 -> 0.25), `glassColor` ("rgba(255,255,255,0.05)" -> "transparent").
200
213
 
201
214
  ## Visibility Note
202
215
 
package/dist/index.cjs CHANGED
@@ -81,9 +81,9 @@ var FRAG = `
81
81
  precision mediump float;
82
82
  uniform vec2 uRes;
83
83
  uniform float uRadius;
84
- uniform float uEdgeSize;
85
- uniform float uIntensity;
86
- uniform float uDistortion;
84
+ uniform float uBorderSoftness;
85
+ uniform float uSpecularWidth;
86
+ uniform int uMode; // 0 = displacement, 1 = specular
87
87
 
88
88
  float sdRoundedBox(vec2 p, vec2 b, float r){
89
89
  r = min(r, min(b.x, b.y));
@@ -91,32 +91,36 @@ float sdRoundedBox(vec2 p, vec2 b, float r){
91
91
  return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
92
92
  }
93
93
 
94
- float getHeight(vec2 p) {
95
- vec2 halfSize = uRes * 0.5 - 2.0;
96
-
97
- // Aumenta o tamanho base do box ligeiramente proporcinal ao edge
98
- halfSize += uEdgeSize * 0.2;
99
-
100
- float d = sdRoundedBox(p, halfSize, uRadius);
101
-
102
- float borderSoftness = uEdgeSize * uIntensity;
103
- d = max(d, -borderSoftness);
104
- return smoothstep(0.0, -borderSoftness, d);
94
+ vec3 calcNormal(vec2 p, vec2 b, float r){
95
+ const float e = 1.0;
96
+ vec2 h = vec2(e, 0.0);
97
+ return normalize(vec3(
98
+ sdRoundedBox(p+h.xy, b, r) - sdRoundedBox(p-h.xy, b, r),
99
+ sdRoundedBox(p+h.yx, b, r) - sdRoundedBox(p-h.yx, b, r),
100
+ -e * 2.0
101
+ ));
105
102
  }
106
103
 
107
104
  void main(){
108
105
  vec2 p = gl_FragCoord.xy - uRes * 0.5;
109
- p.y = -p.y;
106
+ vec2 halfSize = uRes * 0.5 - 1.0;
107
+ float d = sdRoundedBox(p, halfSize, uRadius);
110
108
 
111
- // --- Displacement Map ---
112
- const vec2 e = vec2(1.0, 0.0);
113
- float hx = getHeight(p + e.xy) - getHeight(p - e.xy);
114
- float hy = getHeight(p + e.yx) - getHeight(p - e.yx);
109
+ if(d > 0.0){ gl_FragColor = vec4(0.0); return; }
115
110
 
116
- vec2 normal = vec2(-hx, -hy) * uDistortion;
117
- vec2 color = clamp(normal * 0.5 + 0.5, 0.0, 1.0);
118
-
119
- gl_FragColor = vec4(color.x, color.y, 0.5, 1.0);
111
+ if(uMode == 0){
112
+ vec3 n = calcNormal(p, halfSize, uRadius);
113
+ vec3 nc = n * 0.5 + 0.5;
114
+ float border = smoothstep(-uBorderSoftness, 0.0, d);
115
+ vec3 flat_ = vec3(0.5, 0.5, 1.0);
116
+ gl_FragColor = vec4(mix(flat_, nc, border), 1.0);
117
+ } else {
118
+ float rim = smoothstep(-uSpecularWidth - 2.0, -uSpecularWidth, d)
119
+ * (1.0 - smoothstep(-2.0, 0.0, d));
120
+ float glow = smoothstep(-uBorderSoftness, 0.0, d) * 0.1;
121
+ float s = clamp(rim + glow, 0.0, 1.0);
122
+ gl_FragColor = vec4(vec3(s), s);
123
+ }
120
124
  }
121
125
  `;
122
126
  function compile(gl, type, src) {
@@ -146,33 +150,42 @@ function getGL() {
146
150
  _cachedProgram = { gl, program, canvas };
147
151
  return _cachedProgram;
148
152
  }
149
- function render(width, height, radius, edgeSize, intensity, distortion) {
153
+ function render(width, height, radius, borderSoftness, specularWidth, mode) {
150
154
  const { gl, program, canvas } = getGL();
151
155
  canvas.width = width;
152
156
  canvas.height = height;
153
157
  gl.viewport(0, 0, width, height);
154
- gl.clearColor(0.5, 0.5, 0.5, 1);
158
+ gl.clearColor(0, 0, 0, 0);
155
159
  gl.clear(gl.COLOR_BUFFER_BIT);
156
160
  gl.useProgram(program);
157
161
  gl.uniform2f(gl.getUniformLocation(program, "uRes"), width, height);
158
162
  gl.uniform1f(gl.getUniformLocation(program, "uRadius"), radius);
159
- gl.uniform1f(gl.getUniformLocation(program, "uEdgeSize"), edgeSize);
160
- gl.uniform1f(gl.getUniformLocation(program, "uIntensity"), intensity);
161
- gl.uniform1f(gl.getUniformLocation(program, "uDistortion"), distortion);
163
+ gl.uniform1f(gl.getUniformLocation(program, "uBorderSoftness"), borderSoftness);
164
+ gl.uniform1f(gl.getUniformLocation(program, "uSpecularWidth"), specularWidth);
165
+ gl.uniform1i(gl.getUniformLocation(program, "uMode"), mode);
162
166
  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
163
167
  return canvas.toDataURL("image/png");
164
168
  }
165
169
  function generateGlassMaps(opts) {
166
- const { width, height, radius = 60, edgeSize = 40, intensity = 1, distortion = 15 } = opts;
170
+ const {
171
+ width,
172
+ height,
173
+ radius = 60,
174
+ edgeSize = 30,
175
+ intensity = 0.7,
176
+ specularWidth = 0.02
177
+ } = opts;
167
178
  const r = Math.min(radius, width / 2, height / 2);
179
+ const borderSoftness = edgeSize * intensity;
180
+ const specPx = specularWidth * Math.min(width, height);
168
181
  return {
169
- displacement: render(width, height, r, edgeSize, intensity, distortion)
182
+ displacement: render(width, height, r, borderSoftness, specPx, 0),
183
+ specular: render(width, height, r, borderSoftness, specPx, 1)
170
184
  };
171
185
  }
172
186
 
173
187
  // src/liquid-glass-button.tsx
174
188
  var import_jsx_runtime = require("react/jsx-runtime");
175
- var PADDING_PCT = 50;
176
189
  var LiquidGlassButton = (0, import_react.forwardRef)(
177
190
  function LiquidGlassButton2(_a, ref) {
178
191
  var _b = _a, {
@@ -182,20 +195,18 @@ var LiquidGlassButton = (0, import_react.forwardRef)(
182
195
  width = 300,
183
196
  height = 56,
184
197
  radius = 60,
185
- edgeSize,
186
- intensity,
187
- smoothness = 1,
188
- distortion,
189
- chroma = 3,
190
- glassColor = "rgba(255,255,255,0.05)",
191
- displacement = 35,
192
- blur = 2,
193
- saturation = 1.2,
194
- hoverScale = 1.05,
195
- hoverDisplacement = 65,
198
+ edgeSize = 30,
199
+ intensity = 0.7,
200
+ specularWidth = 0.02,
201
+ displacement = 55,
202
+ blur = 1,
203
+ saturation = 150,
204
+ brightness = 1.1,
205
+ glassColor = "transparent",
206
+ hoverScale = 1.08,
207
+ hoverDisplacement = 125,
196
208
  hoverBlur = 4,
197
- hoverChromaMultiplier = 2.5,
198
- hoverDuration = 0.4,
209
+ hoverDuration = 0.25,
199
210
  disableAnimation = false
200
211
  } = _b, props = __objRest(_b, [
201
212
  "children",
@@ -206,55 +217,55 @@ var LiquidGlassButton = (0, import_react.forwardRef)(
206
217
  "radius",
207
218
  "edgeSize",
208
219
  "intensity",
209
- "smoothness",
210
- "distortion",
211
- "chroma",
212
- "glassColor",
220
+ "specularWidth",
213
221
  "displacement",
214
222
  "blur",
215
223
  "saturation",
224
+ "brightness",
225
+ "glassColor",
216
226
  "hoverScale",
217
227
  "hoverDisplacement",
218
228
  "hoverBlur",
219
- "hoverChromaMultiplier",
220
229
  "hoverDuration",
221
230
  "disableAnimation"
222
231
  ]);
223
232
  const internalRef = (0, import_react.useRef)(null);
224
233
  const buttonRef = ref != null ? ref : internalRef;
234
+ const displacerRef = (0, import_react.useRef)(null);
225
235
  const blurRef = (0, import_react.useRef)(null);
226
- const displacerR = (0, import_react.useRef)(null);
227
- const displacerG = (0, import_react.useRef)(null);
228
- const displacerB = (0, import_react.useRef)(null);
229
236
  const filterId = "lg" + (0, import_react.useId)().replace(/:/g, "");
230
237
  const [maps, setMaps] = (0, import_react.useState)(null);
231
238
  (0, import_react.useEffect)(() => {
232
239
  let cancelled = false;
233
- const m = generateGlassMaps({ width, height, radius, edgeSize, intensity, distortion });
234
- const img = new Image();
235
- img.onload = () => {
236
- if (!cancelled) setMaps(m);
240
+ const m = generateGlassMaps({ width, height, radius, edgeSize, intensity, specularWidth });
241
+ let loaded = 0;
242
+ const onLoad = () => {
243
+ loaded++;
244
+ if (loaded === 2 && !cancelled) setMaps(m);
237
245
  };
238
- img.src = m.displacement;
246
+ const img1 = new Image();
247
+ img1.onload = onLoad;
248
+ img1.src = m.displacement;
249
+ const img2 = new Image();
250
+ img2.onload = onLoad;
251
+ img2.src = m.specular;
239
252
  return () => {
240
253
  cancelled = true;
241
254
  };
242
- }, [width, height, radius, edgeSize, intensity, distortion]);
255
+ }, [width, height, radius, edgeSize, intensity, specularWidth]);
243
256
  (0, import_react.useEffect)(() => {
244
257
  const button = buttonRef.current;
245
- if (!button || !blurRef.current || disableAnimation) return;
258
+ const displacer = displacerRef.current;
259
+ const blurEl = blurRef.current;
260
+ if (!button || !displacer || !blurEl || disableAnimation) return;
246
261
  if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
247
262
  const fx = {
248
263
  displacement,
249
- blur,
250
- chroma
264
+ blur
251
265
  };
252
266
  const sync = () => {
253
- var _a2, _b2, _c, _d;
254
- (_a2 = displacerR.current) == null ? void 0 : _a2.setAttribute("scale", (fx.displacement + fx.chroma).toString());
255
- (_b2 = displacerG.current) == null ? void 0 : _b2.setAttribute("scale", fx.displacement.toString());
256
- (_c = displacerB.current) == null ? void 0 : _c.setAttribute("scale", (fx.displacement - fx.chroma).toString());
257
- (_d = blurRef.current) == null ? void 0 : _d.setAttribute("stdDeviation", fx.blur.toString());
267
+ displacer.setAttribute("scale", fx.displacement.toString());
268
+ blurEl.setAttribute("stdDeviation", fx.blur.toString());
258
269
  };
259
270
  sync();
260
271
  const onEnter = () => {
@@ -262,29 +273,35 @@ var LiquidGlassButton = (0, import_react.forwardRef)(
262
273
  import_gsap.default.to(fx, {
263
274
  displacement: hoverDisplacement,
264
275
  blur: hoverBlur,
265
- chroma: chroma * hoverChromaMultiplier,
266
276
  duration: hoverDuration,
267
- ease: "power3.out",
277
+ ease: "back.out(1.4)",
268
278
  onUpdate: sync
269
279
  });
270
- import_gsap.default.to(button, { scale: hoverScale, duration: hoverDuration, ease: "power3.out" });
280
+ import_gsap.default.to(button, {
281
+ scale: hoverScale,
282
+ duration: hoverDuration,
283
+ ease: "back.out(1.4)"
284
+ });
271
285
  };
272
286
  const onLeave = () => {
273
287
  import_gsap.default.killTweensOf([fx, button]);
274
288
  import_gsap.default.to(fx, {
275
289
  displacement,
276
290
  blur,
277
- chroma,
278
291
  duration: hoverDuration,
279
292
  ease: "power2.out",
280
293
  onUpdate: sync
281
294
  });
282
- import_gsap.default.to(button, { scale: 1, duration: hoverDuration, ease: "power2.out" });
295
+ import_gsap.default.to(button, {
296
+ scale: 1,
297
+ duration: hoverDuration,
298
+ ease: "power2.out"
299
+ });
283
300
  };
284
301
  const onClick = () => {
285
302
  import_gsap.default.killTweensOf(button);
286
303
  const cur = import_gsap.default.getProperty(button, "scale");
287
- import_gsap.default.timeline().to(button, { scale: cur * 0.95, duration: 0.1, ease: "power2.in" }).to(button, { scale: hoverScale, duration: 0.3, ease: "back.out(2)" });
304
+ import_gsap.default.timeline().to(button, { scale: cur * 0.92, duration: 0.08, ease: "power2.in" }).to(button, { scale: hoverScale, duration: 0.25, ease: "back.out(2)" });
288
305
  };
289
306
  button.addEventListener("pointerenter", onEnter);
290
307
  button.addEventListener("pointerleave", onLeave);
@@ -295,48 +312,128 @@ var LiquidGlassButton = (0, import_react.forwardRef)(
295
312
  button.removeEventListener("click", onClick);
296
313
  import_gsap.default.killTweensOf([button, fx]);
297
314
  };
298
- }, [buttonRef, maps, chroma, displacement, blur, hoverScale, hoverDisplacement, hoverBlur, hoverChromaMultiplier, hoverDuration, disableAnimation]);
315
+ }, [buttonRef, maps, displacement, blur, hoverScale, hoverDisplacement, hoverBlur, hoverDuration, disableAnimation]);
299
316
  if (!maps) return null;
300
317
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
301
318
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
302
319
  "button",
303
320
  __spreadProps(__spreadValues({
304
321
  ref: buttonRef,
305
- className: cn("relative overflow-hidden shadow-2xl shadow-black/20 cursor-pointer", className),
322
+ className: cn("relative overflow-hidden shadow-lg cursor-pointer", className),
306
323
  style: __spreadValues({ width, height, borderRadius: radius, border: "none", background: glassColor }, style)
307
324
  }, props), {
308
325
  children: [
309
326
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
310
327
  "div",
311
328
  {
312
- className: "absolute inset-0 z-0",
329
+ className: "absolute inset-0",
313
330
  style: {
331
+ backdropFilter: `url(#${filterId}) brightness(${brightness * 100}%)`,
332
+ WebkitBackdropFilter: `url(#${filterId}) brightness(${brightness * 100}%)`,
314
333
  borderRadius: "inherit",
315
- backdropFilter: `url(#${filterId})`,
316
- WebkitBackdropFilter: `url(#${filterId})`,
317
334
  willChange: "backdrop-filter",
318
335
  transform: "translateZ(0)"
319
336
  }
320
337
  }
321
338
  ),
322
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "absolute inset-0 z-10 flex items-center justify-center font-bold text-white shadow-[inset_0_1px_1px_rgba(255,255,255,0.4)]", style: { background: "linear-gradient(180deg, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.0) 100%)", borderRadius: "inherit" }, children })
339
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
340
+ "div",
341
+ {
342
+ className: "absolute inset-0 inline-flex items-center justify-center font-bold text-white",
343
+ style: { background: "hsl(0 100% 100% / 15%)", borderRadius: "inherit" },
344
+ children
345
+ }
346
+ )
323
347
  ]
324
348
  })
325
349
  ),
326
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { style: { position: "absolute", width: 0, height: 0, pointerEvents: "none" }, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("filter", { id: filterId, x: `-${PADDING_PCT}%`, y: `-${PADDING_PCT}%`, width: `${100 + PADDING_PCT * 2}%`, height: `${100 + PADDING_PCT * 2}%`, colorInterpolationFilters: "sRGB", children: [
327
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("feGaussianBlur", { ref: blurRef, in: "SourceGraphic", stdDeviation: blur, result: "blurred_bg", edgeMode: "duplicate" }),
328
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("feImage", { href: maps.displacement, result: "disp_map", x: `${PADDING_PCT}%`, y: `${PADDING_PCT}%`, width: `${100}%`, height: `${100}%`, preserveAspectRatio: "none" }),
329
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("feGaussianBlur", { in: "disp_map", stdDeviation: smoothness, result: "disp_blurred", edgeMode: "duplicate" }),
330
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("feDisplacementMap", { ref: displacerR, in: "blurred_bg", in2: "disp_blurred", scale: displacement + chroma, xChannelSelector: "R", yChannelSelector: "G", result: "displaced_r" }),
331
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("feColorMatrix", { in: "displaced_r", type: "matrix", values: "1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0", result: "red_channel" }),
332
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("feDisplacementMap", { ref: displacerG, in: "blurred_bg", in2: "disp_blurred", scale: displacement, xChannelSelector: "R", yChannelSelector: "G", result: "displaced_g" }),
333
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("feColorMatrix", { in: "displaced_g", type: "matrix", values: "0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0", result: "green_channel" }),
334
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("feDisplacementMap", { ref: displacerB, in: "blurred_bg", in2: "disp_blurred", scale: displacement - chroma, xChannelSelector: "R", yChannelSelector: "G", result: "displaced_b" }),
335
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("feColorMatrix", { in: "displaced_b", type: "matrix", values: "0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0", result: "blue_channel" }),
336
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("feBlend", { in: "red_channel", in2: "green_channel", mode: "screen", result: "rg_channels" }),
337
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("feBlend", { in: "rg_channels", in2: "blue_channel", mode: "screen", result: "rgb_channels" }),
338
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("feColorMatrix", { in: "rgb_channels", type: "saturate", values: saturation.toString(), result: "final" })
339
- ] }) }) })
350
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
351
+ "svg",
352
+ {
353
+ colorInterpolationFilters: "sRGB",
354
+ style: { position: "absolute", width: 0, height: 0, overflow: "hidden" },
355
+ "aria-hidden": "true",
356
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("filter", { id: filterId, children: [
357
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
358
+ "feGaussianBlur",
359
+ {
360
+ ref: blurRef,
361
+ in: "SourceGraphic",
362
+ stdDeviation: blur,
363
+ result: "blurred_source"
364
+ }
365
+ ),
366
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
367
+ "feImage",
368
+ {
369
+ href: maps.displacement,
370
+ x: "0",
371
+ y: "0",
372
+ width,
373
+ height,
374
+ result: "displacement_map"
375
+ }
376
+ ),
377
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
378
+ "feDisplacementMap",
379
+ {
380
+ ref: displacerRef,
381
+ in: "blurred_source",
382
+ in2: "displacement_map",
383
+ scale: displacement,
384
+ xChannelSelector: "R",
385
+ yChannelSelector: "G",
386
+ result: "displaced"
387
+ }
388
+ ),
389
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
390
+ "feColorMatrix",
391
+ {
392
+ in: "displaced",
393
+ type: "saturate",
394
+ result: "displaced_saturated",
395
+ values: saturation.toString()
396
+ }
397
+ ),
398
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
399
+ "feImage",
400
+ {
401
+ href: maps.specular,
402
+ x: "0",
403
+ y: "0",
404
+ width,
405
+ height,
406
+ result: "specular_layer"
407
+ }
408
+ ),
409
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
410
+ "feGaussianBlur",
411
+ {
412
+ in: "specular_layer",
413
+ stdDeviation: "1",
414
+ result: "blurred_specular_layer"
415
+ }
416
+ ),
417
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
418
+ "feComposite",
419
+ {
420
+ in: "displaced_saturated",
421
+ in2: "blurred_specular_layer",
422
+ operator: "in",
423
+ result: "final_specular_layer"
424
+ }
425
+ ),
426
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
427
+ "feBlend",
428
+ {
429
+ in: "final_specular_layer",
430
+ in2: "displaced",
431
+ mode: "normal"
432
+ }
433
+ )
434
+ ] }) })
435
+ }
436
+ )
340
437
  ] });
341
438
  }
342
439
  );