@marcosdemik/liquidglass 2.0.0 → 2.0.2

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
@@ -78,7 +78,7 @@ function cn(...classes) {
78
78
  // src/generate-displacement-map.ts
79
79
  var VERT = `attribute vec4 position; void main(){ gl_Position = position; }`;
80
80
  var FRAG = `
81
- precision mediump float;
81
+ precision highp float;
82
82
  uniform vec2 uRes;
83
83
  uniform float uRadius;
84
84
  uniform float uBorderSoftness;
@@ -92,7 +92,7 @@ float sdRoundedBox(vec2 p, vec2 b, float r){
92
92
  }
93
93
 
94
94
  vec3 calcNormal(vec2 p, vec2 b, float r){
95
- const float e = 1.0;
95
+ float e = max(0.5, min(b.x, b.y) * 0.01);
96
96
  vec2 h = vec2(e, 0.0);
97
97
  return normalize(vec3(
98
98
  sdRoundedBox(p+h.xy, b, r) - sdRoundedBox(p-h.xy, b, r),
@@ -173,14 +173,18 @@ function generateGlassMaps(opts) {
173
173
  radius = 60,
174
174
  edgeSize = 30,
175
175
  intensity = 0.7,
176
- specularWidth = 0.02
176
+ specularWidth = 0.02,
177
+ quality = 2
177
178
  } = opts;
178
- const r = Math.min(radius, width / 2, height / 2);
179
- const borderSoftness = edgeSize * intensity;
180
- const specPx = specularWidth * Math.min(width, height);
179
+ const scale = Math.max(1, Math.round(quality));
180
+ const rw = width * scale;
181
+ const rh = height * scale;
182
+ const r = Math.min(radius * scale, rw / 2, rh / 2);
183
+ const borderSoftness = edgeSize * intensity * scale;
184
+ const specPx = specularWidth * Math.min(rw, rh);
181
185
  return {
182
- displacement: render(width, height, r, borderSoftness, specPx, 0),
183
- specular: render(width, height, r, borderSoftness, specPx, 1)
186
+ displacement: render(rw, rh, r, borderSoftness, specPx, 0),
187
+ specular: render(rw, rh, r, borderSoftness, specPx, 1)
184
188
  };
185
189
  }
186
190
 
@@ -207,7 +211,8 @@ var LiquidGlassButton = (0, import_react.forwardRef)(
207
211
  hoverDisplacement = 125,
208
212
  hoverBlur = 4,
209
213
  hoverDuration = 0.25,
210
- disableAnimation = false
214
+ disableAnimation = false,
215
+ quality = 2
211
216
  } = _b, props = __objRest(_b, [
212
217
  "children",
213
218
  "className",
@@ -227,7 +232,8 @@ var LiquidGlassButton = (0, import_react.forwardRef)(
227
232
  "hoverDisplacement",
228
233
  "hoverBlur",
229
234
  "hoverDuration",
230
- "disableAnimation"
235
+ "disableAnimation",
236
+ "quality"
231
237
  ]);
232
238
  const internalRef = (0, import_react.useRef)(null);
233
239
  const buttonRef = ref != null ? ref : internalRef;
@@ -237,7 +243,7 @@ var LiquidGlassButton = (0, import_react.forwardRef)(
237
243
  const [maps, setMaps] = (0, import_react.useState)(null);
238
244
  (0, import_react.useEffect)(() => {
239
245
  let cancelled = false;
240
- const m = generateGlassMaps({ width, height, radius, edgeSize, intensity, specularWidth });
246
+ const m = generateGlassMaps({ width, height, radius, edgeSize, intensity, specularWidth, quality });
241
247
  let loaded = 0;
242
248
  const onLoad = () => {
243
249
  loaded++;
@@ -252,7 +258,7 @@ var LiquidGlassButton = (0, import_react.forwardRef)(
252
258
  return () => {
253
259
  cancelled = true;
254
260
  };
255
- }, [width, height, radius, edgeSize, intensity, specularWidth]);
261
+ }, [width, height, radius, edgeSize, intensity, specularWidth, quality]);
256
262
  (0, import_react.useEffect)(() => {
257
263
  const button = buttonRef.current;
258
264
  const displacer = displacerRef.current;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/liquid-glass-button.tsx","../src/utils.ts","../src/generate-displacement-map.ts"],"sourcesContent":["export { LiquidGlassButton } from \"./liquid-glass-button\";\nexport type { LiquidGlassButtonProps } from \"./liquid-glass-button\";\nexport { generateGlassMaps } from \"./generate-displacement-map\";\nexport type { MapOptions } from \"./generate-displacement-map\";\nexport { cn } from \"./utils\";\n","import React, { useRef, useEffect, useState, useId, forwardRef } from \"react\";\nimport gsap from \"gsap\";\nimport { cn } from \"./utils\";\nimport { generateGlassMaps } from \"./generate-displacement-map\";\n\nexport interface LiquidGlassButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Button width in px */\n width?: number;\n /** Button height in px */\n height?: number;\n /** Border radius in px */\n radius?: number;\n /** Edge thickness of the glass refraction zone */\n edgeSize?: number;\n /** Edge refraction intensity (0-1) */\n intensity?: number;\n /** Specular rim thickness relative to size (0-1) */\n specularWidth?: number;\n /** feDisplacementMap scale - how much the background refracts */\n displacement?: number;\n /** Gaussian blur applied to the background */\n blur?: number;\n /** Saturation applied to the displaced result */\n saturation?: number;\n /** Brightness boost on the backdrop-filter (1 = normal) */\n brightness?: number;\n /** Background tint color of the glass */\n glassColor?: string;\n /** Scale multiplier on hover */\n hoverScale?: number;\n /** Displacement scale on hover */\n hoverDisplacement?: number;\n /** Blur amount on hover */\n hoverBlur?: number;\n /** Duration of hover animation in seconds */\n hoverDuration?: number;\n /** Disable all GSAP animations */\n disableAnimation?: boolean;\n}\n\nexport const LiquidGlassButton = forwardRef<HTMLButtonElement, LiquidGlassButtonProps>(\n function LiquidGlassButton({\n children, className, style,\n width = 300,\n height = 56,\n radius = 60,\n edgeSize = 30,\n intensity = 0.7,\n specularWidth = 0.02,\n displacement = 55,\n blur = 1,\n saturation = 150,\n brightness = 1.1,\n glassColor = \"transparent\",\n hoverScale = 1.08,\n hoverDisplacement = 125,\n hoverBlur = 4,\n hoverDuration = 0.25,\n disableAnimation = false,\n ...props\n }, ref) {\n const internalRef = useRef<HTMLButtonElement>(null);\n const buttonRef = (ref as React.RefObject<HTMLButtonElement>) ?? internalRef;\n\n const displacerRef = useRef<SVGFEDisplacementMapElement>(null);\n const blurRef = useRef<SVGFEGaussianBlurElement>(null);\n\n const filterId = \"lg\" + useId().replace(/:/g, \"\");\n\n const [maps, setMaps] = useState<ReturnType<typeof generateGlassMaps> | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n const m = generateGlassMaps({ width, height, radius, edgeSize, intensity, specularWidth });\n\n // Pre-decode both PNGs\n let loaded = 0;\n const onLoad = () => {\n loaded++;\n if (loaded === 2 && !cancelled) setMaps(m);\n };\n\n const img1 = new Image();\n img1.onload = onLoad;\n img1.src = m.displacement;\n\n const img2 = new Image();\n img2.onload = onLoad;\n img2.src = m.specular;\n\n return () => { cancelled = true; };\n }, [width, height, radius, edgeSize, intensity, specularWidth]);\n\n useEffect(() => {\n const button = buttonRef.current;\n const displacer = displacerRef.current;\n const blurEl = blurRef.current;\n if (!button || !displacer || !blurEl || disableAnimation) return;\n\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n\n const fx = {\n displacement: displacement,\n blur: blur,\n };\n\n const sync = () => {\n displacer.setAttribute(\"scale\", fx.displacement.toString());\n blurEl.setAttribute(\"stdDeviation\", fx.blur.toString());\n };\n\n sync();\n\n const onEnter = () => {\n gsap.killTweensOf([fx, button]);\n gsap.to(fx, {\n displacement: hoverDisplacement,\n blur: hoverBlur,\n duration: hoverDuration,\n ease: \"back.out(1.4)\",\n onUpdate: sync,\n });\n gsap.to(button, {\n scale: hoverScale,\n duration: hoverDuration,\n ease: \"back.out(1.4)\",\n });\n };\n\n const onLeave = () => {\n gsap.killTweensOf([fx, button]);\n gsap.to(fx, {\n displacement: displacement,\n blur: blur,\n duration: hoverDuration,\n ease: \"power2.out\",\n onUpdate: sync,\n });\n gsap.to(button, {\n scale: 1,\n duration: hoverDuration,\n ease: \"power2.out\",\n });\n };\n\n const onClick = () => {\n gsap.killTweensOf(button);\n const cur = gsap.getProperty(button, \"scale\") as number;\n gsap.timeline()\n .to(button, { scale: cur * 0.92, duration: 0.08, ease: \"power2.in\" })\n .to(button, { scale: hoverScale, duration: 0.25, ease: \"back.out(2)\" });\n };\n\n button.addEventListener(\"pointerenter\", onEnter);\n button.addEventListener(\"pointerleave\", onLeave);\n button.addEventListener(\"click\", onClick);\n\n return () => {\n button.removeEventListener(\"pointerenter\", onEnter);\n button.removeEventListener(\"pointerleave\", onLeave);\n button.removeEventListener(\"click\", onClick);\n gsap.killTweensOf([button, fx]);\n };\n }, [buttonRef, maps, displacement, blur, hoverScale, hoverDisplacement, hoverBlur, hoverDuration, disableAnimation]);\n\n if (!maps) return null;\n\n return (\n <>\n <button\n ref={buttonRef}\n className={cn(\"relative overflow-hidden shadow-lg cursor-pointer\", className)}\n style={{ width, height, borderRadius: radius, border: \"none\", background: glassColor, ...style }}\n {...props}\n >\n <div\n className=\"absolute inset-0\"\n style={{\n backdropFilter: `url(#${filterId}) brightness(${brightness * 100}%)`,\n WebkitBackdropFilter: `url(#${filterId}) brightness(${brightness * 100}%)`,\n borderRadius: \"inherit\",\n willChange: \"backdrop-filter\",\n transform: \"translateZ(0)\",\n }}\n />\n <div\n className=\"absolute inset-0 inline-flex items-center justify-center font-bold text-white\"\n style={{ background: \"hsl(0 100% 100% / 15%)\", borderRadius: \"inherit\" }}\n >\n {children}\n </div>\n </button>\n\n <svg\n colorInterpolationFilters=\"sRGB\"\n style={{ position: \"absolute\", width: 0, height: 0, overflow: \"hidden\" }}\n aria-hidden=\"true\"\n >\n <defs>\n <filter id={filterId}>\n <feGaussianBlur\n ref={blurRef}\n in=\"SourceGraphic\"\n stdDeviation={blur}\n result=\"blurred_source\"\n />\n <feImage\n href={maps.displacement}\n x=\"0\"\n y=\"0\"\n width={width}\n height={height}\n result=\"displacement_map\"\n />\n <feDisplacementMap\n ref={displacerRef}\n in=\"blurred_source\"\n in2=\"displacement_map\"\n scale={displacement}\n xChannelSelector=\"R\"\n yChannelSelector=\"G\"\n result=\"displaced\"\n />\n <feColorMatrix\n in=\"displaced\"\n type=\"saturate\"\n result=\"displaced_saturated\"\n values={saturation.toString()}\n />\n <feImage\n href={maps.specular}\n x=\"0\"\n y=\"0\"\n width={width}\n height={height}\n result=\"specular_layer\"\n />\n <feGaussianBlur\n in=\"specular_layer\"\n stdDeviation=\"1\"\n result=\"blurred_specular_layer\"\n />\n <feComposite\n in=\"displaced_saturated\"\n in2=\"blurred_specular_layer\"\n operator=\"in\"\n result=\"final_specular_layer\"\n />\n <feBlend\n in=\"final_specular_layer\"\n in2=\"displaced\"\n mode=\"normal\"\n />\n </filter>\n </defs>\n </svg>\n </>\n );\n }\n);\n","export function cn(...classes: (string | undefined | null | false)[]) {\n return classes.filter(Boolean).join(\" \");\n}\n","export interface MapOptions {\n width: number;\n height: number;\n radius?: number;\n edgeSize?: number;\n intensity?: number;\n specularWidth?: number;\n}\n\nconst VERT = `attribute vec4 position; void main(){ gl_Position = position; }`;\n\nconst FRAG = `\nprecision mediump float;\nuniform vec2 uRes;\nuniform float uRadius;\nuniform float uBorderSoftness;\nuniform float uSpecularWidth;\nuniform int uMode; // 0 = displacement, 1 = specular\n\nfloat sdRoundedBox(vec2 p, vec2 b, float r){\n r = min(r, min(b.x, b.y));\n vec2 q = abs(p) - b + r;\n return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;\n}\n\nvec3 calcNormal(vec2 p, vec2 b, float r){\n const float e = 1.0;\n vec2 h = vec2(e, 0.0);\n return normalize(vec3(\n sdRoundedBox(p+h.xy, b, r) - sdRoundedBox(p-h.xy, b, r),\n sdRoundedBox(p+h.yx, b, r) - sdRoundedBox(p-h.yx, b, r),\n -e * 2.0\n ));\n}\n\nvoid main(){\n vec2 p = gl_FragCoord.xy - uRes * 0.5;\n vec2 halfSize = uRes * 0.5 - 1.0;\n float d = sdRoundedBox(p, halfSize, uRadius);\n\n if(d > 0.0){ gl_FragColor = vec4(0.0); return; }\n\n if(uMode == 0){\n vec3 n = calcNormal(p, halfSize, uRadius);\n vec3 nc = n * 0.5 + 0.5;\n float border = smoothstep(-uBorderSoftness, 0.0, d);\n vec3 flat_ = vec3(0.5, 0.5, 1.0);\n gl_FragColor = vec4(mix(flat_, nc, border), 1.0);\n } else {\n float rim = smoothstep(-uSpecularWidth - 2.0, -uSpecularWidth, d)\n * (1.0 - smoothstep(-2.0, 0.0, d));\n float glow = smoothstep(-uBorderSoftness, 0.0, d) * 0.1;\n float s = clamp(rim + glow, 0.0, 1.0);\n gl_FragColor = vec4(vec3(s), s);\n }\n}\n`;\n\nfunction compile(gl: WebGLRenderingContext, type: number, src: string) {\n const s = gl.createShader(type)!;\n gl.shaderSource(s, src);\n gl.compileShader(s);\n return s;\n}\n\nlet _cachedProgram: { gl: WebGLRenderingContext; program: WebGLProgram; canvas: HTMLCanvasElement } | null = null;\n\nfunction getGL() {\n if (_cachedProgram) return _cachedProgram;\n\n const canvas = document.createElement(\"canvas\");\n const gl = canvas.getContext(\"webgl\", { preserveDrawingBuffer: true, premultipliedAlpha: false })!;\n\n const vs = compile(gl, gl.VERTEX_SHADER, VERT);\n const fs = compile(gl, gl.FRAGMENT_SHADER, FRAG);\n const program = gl.createProgram()!;\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n gl.useProgram(program);\n\n const buf = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, buf);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);\n const pos = gl.getAttribLocation(program, \"position\");\n gl.enableVertexAttribArray(pos);\n gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0);\n\n _cachedProgram = { gl, program, canvas };\n return _cachedProgram;\n}\n\nfunction render(\n width: number,\n height: number,\n radius: number,\n borderSoftness: number,\n specularWidth: number,\n mode: number,\n): string {\n const { gl, program, canvas } = getGL();\n canvas.width = width;\n canvas.height = height;\n gl.viewport(0, 0, width, height);\n gl.clearColor(0, 0, 0, 0);\n gl.clear(gl.COLOR_BUFFER_BIT);\n gl.useProgram(program);\n\n gl.uniform2f(gl.getUniformLocation(program, \"uRes\"), width, height);\n gl.uniform1f(gl.getUniformLocation(program, \"uRadius\"), radius);\n gl.uniform1f(gl.getUniformLocation(program, \"uBorderSoftness\"), borderSoftness);\n gl.uniform1f(gl.getUniformLocation(program, \"uSpecularWidth\"), specularWidth);\n gl.uniform1i(gl.getUniformLocation(program, \"uMode\"), mode);\n\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n return canvas.toDataURL(\"image/png\");\n}\n\nexport function generateGlassMaps(opts: MapOptions): {\n displacement: string;\n specular: string;\n} {\n const {\n width,\n height,\n radius = 60,\n edgeSize = 30,\n intensity = 0.7,\n specularWidth = 0.02,\n } = opts;\n\n const r = Math.min(radius, width / 2, height / 2);\n const borderSoftness = edgeSize * intensity;\n const specPx = specularWidth * Math.min(width, height);\n\n return {\n displacement: render(width, height, r, borderSoftness, specPx, 0),\n specular: render(width, height, r, borderSoftness, specPx, 1),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAsE;AACtE,kBAAiB;;;ACDV,SAAS,MAAM,SAAgD;AACpE,SAAO,QAAQ,OAAO,OAAO,EAAE,KAAK,GAAG;AACzC;;;ACOA,IAAM,OAAO;AAEb,IAAM,OAAO;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;AA+Cb,SAAS,QAAQ,IAA2B,MAAc,KAAa;AACrE,QAAM,IAAI,GAAG,aAAa,IAAI;AAC9B,KAAG,aAAa,GAAG,GAAG;AACtB,KAAG,cAAc,CAAC;AAClB,SAAO;AACT;AAEA,IAAI,iBAAyG;AAE7G,SAAS,QAAQ;AACf,MAAI,eAAgB,QAAO;AAE3B,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,QAAM,KAAK,OAAO,WAAW,SAAS,EAAE,uBAAuB,MAAM,oBAAoB,MAAM,CAAC;AAEhG,QAAM,KAAK,QAAQ,IAAI,GAAG,eAAe,IAAI;AAC7C,QAAM,KAAK,QAAQ,IAAI,GAAG,iBAAiB,IAAI;AAC/C,QAAM,UAAU,GAAG,cAAc;AACjC,KAAG,aAAa,SAAS,EAAE;AAC3B,KAAG,aAAa,SAAS,EAAE;AAC3B,KAAG,YAAY,OAAO;AACtB,KAAG,WAAW,OAAO;AAErB,QAAM,MAAM,GAAG,aAAa;AAC5B,KAAG,WAAW,GAAG,cAAc,GAAG;AAClC,KAAG,WAAW,GAAG,cAAc,IAAI,aAAa,CAAC,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,WAAW;AAC7F,QAAM,MAAM,GAAG,kBAAkB,SAAS,UAAU;AACpD,KAAG,wBAAwB,GAAG;AAC9B,KAAG,oBAAoB,KAAK,GAAG,GAAG,OAAO,OAAO,GAAG,CAAC;AAEpD,mBAAiB,EAAE,IAAI,SAAS,OAAO;AACvC,SAAO;AACT;AAEA,SAAS,OACP,OACA,QACA,QACA,gBACA,eACA,MACQ;AACR,QAAM,EAAE,IAAI,SAAS,OAAO,IAAI,MAAM;AACtC,SAAO,QAAQ;AACf,SAAO,SAAS;AAChB,KAAG,SAAS,GAAG,GAAG,OAAO,MAAM;AAC/B,KAAG,WAAW,GAAG,GAAG,GAAG,CAAC;AACxB,KAAG,MAAM,GAAG,gBAAgB;AAC5B,KAAG,WAAW,OAAO;AAErB,KAAG,UAAU,GAAG,mBAAmB,SAAS,MAAM,GAAG,OAAO,MAAM;AAClE,KAAG,UAAU,GAAG,mBAAmB,SAAS,SAAS,GAAG,MAAM;AAC9D,KAAG,UAAU,GAAG,mBAAmB,SAAS,iBAAiB,GAAG,cAAc;AAC9E,KAAG,UAAU,GAAG,mBAAmB,SAAS,gBAAgB,GAAG,aAAa;AAC5E,KAAG,UAAU,GAAG,mBAAmB,SAAS,OAAO,GAAG,IAAI;AAE1D,KAAG,WAAW,GAAG,gBAAgB,GAAG,CAAC;AACrC,SAAO,OAAO,UAAU,WAAW;AACrC;AAEO,SAAS,kBAAkB,MAGhC;AACA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,gBAAgB;AAAA,EAClB,IAAI;AAEJ,QAAM,IAAI,KAAK,IAAI,QAAQ,QAAQ,GAAG,SAAS,CAAC;AAChD,QAAM,iBAAiB,WAAW;AAClC,QAAM,SAAS,gBAAgB,KAAK,IAAI,OAAO,MAAM;AAErD,SAAO;AAAA,IACL,cAAc,OAAO,OAAO,QAAQ,GAAG,gBAAgB,QAAQ,CAAC;AAAA,IAChE,UAAU,OAAO,OAAO,QAAQ,GAAG,gBAAgB,QAAQ,CAAC;AAAA,EAC9D;AACF;;;AF6BM;AAhIC,IAAM,wBAAoB;AAAA,EAC/B,SAASA,mBAAkB,IAmBxB,KAAK;AAnBmB,iBACzB;AAAA;AAAA,MAAU;AAAA,MAAW;AAAA,MACrB,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,SAAS;AAAA,MACT,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,eAAe;AAAA,MACf,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,MACb,aAAa;AAAA,MACb,aAAa;AAAA,MACb,oBAAoB;AAAA,MACpB,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,mBAAmB;AAAA,IA1DvB,IAyC6B,IAkBtB,kBAlBsB,IAkBtB;AAAA,MAjBH;AAAA,MAAU;AAAA,MAAW;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAGA,UAAM,kBAAc,qBAA0B,IAAI;AAClD,UAAM,YAAa,oBAA8C;AAEjE,UAAM,mBAAe,qBAAoC,IAAI;AAC7D,UAAM,cAAU,qBAAiC,IAAI;AAErD,UAAM,WAAW,WAAO,oBAAM,EAAE,QAAQ,MAAM,EAAE;AAEhD,UAAM,CAAC,MAAM,OAAO,QAAI,uBAAsD,IAAI;AAElF,gCAAU,MAAM;AACd,UAAI,YAAY;AAChB,YAAM,IAAI,kBAAkB,EAAE,OAAO,QAAQ,QAAQ,UAAU,WAAW,cAAc,CAAC;AAGzF,UAAI,SAAS;AACb,YAAM,SAAS,MAAM;AACnB;AACA,YAAI,WAAW,KAAK,CAAC,UAAW,SAAQ,CAAC;AAAA,MAC3C;AAEA,YAAM,OAAO,IAAI,MAAM;AACvB,WAAK,SAAS;AACd,WAAK,MAAM,EAAE;AAEb,YAAM,OAAO,IAAI,MAAM;AACvB,WAAK,SAAS;AACd,WAAK,MAAM,EAAE;AAEb,aAAO,MAAM;AAAE,oBAAY;AAAA,MAAM;AAAA,IACnC,GAAG,CAAC,OAAO,QAAQ,QAAQ,UAAU,WAAW,aAAa,CAAC;AAE9D,gCAAU,MAAM;AACd,YAAM,SAAS,UAAU;AACzB,YAAM,YAAY,aAAa;AAC/B,YAAM,SAAS,QAAQ;AACvB,UAAI,CAAC,UAAU,CAAC,aAAa,CAAC,UAAU,iBAAkB;AAE1D,UAAI,OAAO,WAAW,kCAAkC,EAAE,QAAS;AAEnE,YAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAEA,YAAM,OAAO,MAAM;AACjB,kBAAU,aAAa,SAAS,GAAG,aAAa,SAAS,CAAC;AAC1D,eAAO,aAAa,gBAAgB,GAAG,KAAK,SAAS,CAAC;AAAA,MACxD;AAEA,WAAK;AAEL,YAAM,UAAU,MAAM;AACpB,oBAAAC,QAAK,aAAa,CAAC,IAAI,MAAM,CAAC;AAC9B,oBAAAA,QAAK,GAAG,IAAI;AAAA,UACV,cAAc;AAAA,UACd,MAAM;AAAA,UACN,UAAU;AAAA,UACV,MAAM;AAAA,UACN,UAAU;AAAA,QACZ,CAAC;AACD,oBAAAA,QAAK,GAAG,QAAQ;AAAA,UACd,OAAO;AAAA,UACP,UAAU;AAAA,UACV,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAEA,YAAM,UAAU,MAAM;AACpB,oBAAAA,QAAK,aAAa,CAAC,IAAI,MAAM,CAAC;AAC9B,oBAAAA,QAAK,GAAG,IAAI;AAAA,UACV;AAAA,UACA;AAAA,UACA,UAAU;AAAA,UACV,MAAM;AAAA,UACN,UAAU;AAAA,QACZ,CAAC;AACD,oBAAAA,QAAK,GAAG,QAAQ;AAAA,UACd,OAAO;AAAA,UACP,UAAU;AAAA,UACV,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAEA,YAAM,UAAU,MAAM;AACpB,oBAAAA,QAAK,aAAa,MAAM;AACxB,cAAM,MAAM,YAAAA,QAAK,YAAY,QAAQ,OAAO;AAC5C,oBAAAA,QAAK,SAAS,EACX,GAAG,QAAQ,EAAE,OAAO,MAAM,MAAM,UAAU,MAAM,MAAM,YAAY,CAAC,EACnE,GAAG,QAAQ,EAAE,OAAO,YAAY,UAAU,MAAM,MAAM,cAAc,CAAC;AAAA,MAC1E;AAEA,aAAO,iBAAiB,gBAAgB,OAAO;AAC/C,aAAO,iBAAiB,gBAAgB,OAAO;AAC/C,aAAO,iBAAiB,SAAS,OAAO;AAExC,aAAO,MAAM;AACX,eAAO,oBAAoB,gBAAgB,OAAO;AAClD,eAAO,oBAAoB,gBAAgB,OAAO;AAClD,eAAO,oBAAoB,SAAS,OAAO;AAC3C,oBAAAA,QAAK,aAAa,CAAC,QAAQ,EAAE,CAAC;AAAA,MAChC;AAAA,IACF,GAAG,CAAC,WAAW,MAAM,cAAc,MAAM,YAAY,mBAAmB,WAAW,eAAe,gBAAgB,CAAC;AAEnH,QAAI,CAAC,KAAM,QAAO;AAElB,WACE,4EACE;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,WAAW,GAAG,qDAAqD,SAAS;AAAA,UAC5E,OAAO,iBAAE,OAAO,QAAQ,cAAc,QAAQ,QAAQ,QAAQ,YAAY,cAAe;AAAA,WACrF,QAJL;AAAA,UAMC;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,OAAO;AAAA,kBACL,gBAAgB,QAAQ,QAAQ,gBAAgB,aAAa,GAAG;AAAA,kBAChE,sBAAsB,QAAQ,QAAQ,gBAAgB,aAAa,GAAG;AAAA,kBACtE,cAAc;AAAA,kBACd,YAAY;AAAA,kBACZ,WAAW;AAAA,gBACb;AAAA;AAAA,YACF;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,OAAO,EAAE,YAAY,0BAA0B,cAAc,UAAU;AAAA,gBAEtE;AAAA;AAAA,YACH;AAAA;AAAA;AAAA,MACF;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,2BAA0B;AAAA,UAC1B,OAAO,EAAE,UAAU,YAAY,OAAO,GAAG,QAAQ,GAAG,UAAU,SAAS;AAAA,UACvE,eAAY;AAAA,UAEZ,sDAAC,UACC,uDAAC,YAAO,IAAI,UACV;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,KAAK;AAAA,gBACL,IAAG;AAAA,gBACH,cAAc;AAAA,gBACd,QAAO;AAAA;AAAA,YACT;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM,KAAK;AAAA,gBACX,GAAE;AAAA,gBACF,GAAE;AAAA,gBACF;AAAA,gBACA;AAAA,gBACA,QAAO;AAAA;AAAA,YACT;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,KAAK;AAAA,gBACL,IAAG;AAAA,gBACH,KAAI;AAAA,gBACJ,OAAO;AAAA,gBACP,kBAAiB;AAAA,gBACjB,kBAAiB;AAAA,gBACjB,QAAO;AAAA;AAAA,YACT;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,MAAK;AAAA,gBACL,QAAO;AAAA,gBACP,QAAQ,WAAW,SAAS;AAAA;AAAA,YAC9B;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM,KAAK;AAAA,gBACX,GAAE;AAAA,gBACF,GAAE;AAAA,gBACF;AAAA,gBACA;AAAA,gBACA,QAAO;AAAA;AAAA,YACT;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,cAAa;AAAA,gBACb,QAAO;AAAA;AAAA,YACT;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,KAAI;AAAA,gBACJ,UAAS;AAAA,gBACT,QAAO;AAAA;AAAA,YACT;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,KAAI;AAAA,gBACJ,MAAK;AAAA;AAAA,YACP;AAAA,aACF,GACF;AAAA;AAAA,MACF;AAAA,OACF;AAAA,EAEJ;AACF;","names":["LiquidGlassButton","gsap"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/liquid-glass-button.tsx","../src/utils.ts","../src/generate-displacement-map.ts"],"sourcesContent":["export { LiquidGlassButton } from \"./liquid-glass-button\";\nexport type { LiquidGlassButtonProps } from \"./liquid-glass-button\";\nexport { generateGlassMaps } from \"./generate-displacement-map\";\nexport type { MapOptions } from \"./generate-displacement-map\";\nexport { cn } from \"./utils\";\n","import React, { useRef, useEffect, useState, useId, forwardRef } from \"react\";\nimport gsap from \"gsap\";\nimport { cn } from \"./utils\";\nimport { generateGlassMaps } from \"./generate-displacement-map\";\n\nexport interface LiquidGlassButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Button width in px */\n width?: number;\n /** Button height in px */\n height?: number;\n /** Border radius in px */\n radius?: number;\n /** Edge thickness of the glass refraction zone */\n edgeSize?: number;\n /** Edge refraction intensity (0-1) */\n intensity?: number;\n /** Specular rim thickness relative to size (0-1) */\n specularWidth?: number;\n /** feDisplacementMap scale - how much the background refracts */\n displacement?: number;\n /** Gaussian blur applied to the background */\n blur?: number;\n /** Saturation applied to the displaced result */\n saturation?: number;\n /** Brightness boost on the backdrop-filter (1 = normal) */\n brightness?: number;\n /** Background tint color of the glass */\n glassColor?: string;\n /** Scale multiplier on hover */\n hoverScale?: number;\n /** Displacement scale on hover */\n hoverDisplacement?: number;\n /** Blur amount on hover */\n hoverBlur?: number;\n /** Duration of hover animation in seconds */\n hoverDuration?: number;\n /** Disable all GSAP animations */\n disableAnimation?: boolean;\n /** Supersampling quality for the displacement map (default 2, higher = smoother) */\n quality?: number;\n}\n\nexport const LiquidGlassButton = forwardRef<HTMLButtonElement, LiquidGlassButtonProps>(\n function LiquidGlassButton({\n children, className, style,\n width = 300,\n height = 56,\n radius = 60,\n edgeSize = 30,\n intensity = 0.7,\n specularWidth = 0.02,\n displacement = 55,\n blur = 1,\n saturation = 150,\n brightness = 1.1,\n glassColor = \"transparent\",\n hoverScale = 1.08,\n hoverDisplacement = 125,\n hoverBlur = 4,\n hoverDuration = 0.25,\n disableAnimation = false,\n quality = 2,\n ...props\n }, ref) {\n const internalRef = useRef<HTMLButtonElement>(null);\n const buttonRef = (ref as React.RefObject<HTMLButtonElement>) ?? internalRef;\n\n const displacerRef = useRef<SVGFEDisplacementMapElement>(null);\n const blurRef = useRef<SVGFEGaussianBlurElement>(null);\n\n const filterId = \"lg\" + useId().replace(/:/g, \"\");\n\n const [maps, setMaps] = useState<ReturnType<typeof generateGlassMaps> | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n const m = generateGlassMaps({ width, height, radius, edgeSize, intensity, specularWidth, quality });\n\n // Pre-decode both PNGs\n let loaded = 0;\n const onLoad = () => {\n loaded++;\n if (loaded === 2 && !cancelled) setMaps(m);\n };\n\n const img1 = new Image();\n img1.onload = onLoad;\n img1.src = m.displacement;\n\n const img2 = new Image();\n img2.onload = onLoad;\n img2.src = m.specular;\n\n return () => { cancelled = true; };\n }, [width, height, radius, edgeSize, intensity, specularWidth, quality]);\n\n useEffect(() => {\n const button = buttonRef.current;\n const displacer = displacerRef.current;\n const blurEl = blurRef.current;\n if (!button || !displacer || !blurEl || disableAnimation) return;\n\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n\n const fx = {\n displacement: displacement,\n blur: blur,\n };\n\n const sync = () => {\n displacer.setAttribute(\"scale\", fx.displacement.toString());\n blurEl.setAttribute(\"stdDeviation\", fx.blur.toString());\n };\n\n sync();\n\n const onEnter = () => {\n gsap.killTweensOf([fx, button]);\n gsap.to(fx, {\n displacement: hoverDisplacement,\n blur: hoverBlur,\n duration: hoverDuration,\n ease: \"back.out(1.4)\",\n onUpdate: sync,\n });\n gsap.to(button, {\n scale: hoverScale,\n duration: hoverDuration,\n ease: \"back.out(1.4)\",\n });\n };\n\n const onLeave = () => {\n gsap.killTweensOf([fx, button]);\n gsap.to(fx, {\n displacement: displacement,\n blur: blur,\n duration: hoverDuration,\n ease: \"power2.out\",\n onUpdate: sync,\n });\n gsap.to(button, {\n scale: 1,\n duration: hoverDuration,\n ease: \"power2.out\",\n });\n };\n\n const onClick = () => {\n gsap.killTweensOf(button);\n const cur = gsap.getProperty(button, \"scale\") as number;\n gsap.timeline()\n .to(button, { scale: cur * 0.92, duration: 0.08, ease: \"power2.in\" })\n .to(button, { scale: hoverScale, duration: 0.25, ease: \"back.out(2)\" });\n };\n\n button.addEventListener(\"pointerenter\", onEnter);\n button.addEventListener(\"pointerleave\", onLeave);\n button.addEventListener(\"click\", onClick);\n\n return () => {\n button.removeEventListener(\"pointerenter\", onEnter);\n button.removeEventListener(\"pointerleave\", onLeave);\n button.removeEventListener(\"click\", onClick);\n gsap.killTweensOf([button, fx]);\n };\n }, [buttonRef, maps, displacement, blur, hoverScale, hoverDisplacement, hoverBlur, hoverDuration, disableAnimation]);\n\n if (!maps) return null;\n\n return (\n <>\n <button\n ref={buttonRef}\n className={cn(\"relative overflow-hidden shadow-lg cursor-pointer\", className)}\n style={{ width, height, borderRadius: radius, border: \"none\", background: glassColor, ...style }}\n {...props}\n >\n <div\n className=\"absolute inset-0\"\n style={{\n backdropFilter: `url(#${filterId}) brightness(${brightness * 100}%)`,\n WebkitBackdropFilter: `url(#${filterId}) brightness(${brightness * 100}%)`,\n borderRadius: \"inherit\",\n willChange: \"backdrop-filter\",\n transform: \"translateZ(0)\",\n }}\n />\n <div\n className=\"absolute inset-0 inline-flex items-center justify-center font-bold text-white\"\n style={{ background: \"hsl(0 100% 100% / 15%)\", borderRadius: \"inherit\" }}\n >\n {children}\n </div>\n </button>\n\n <svg\n colorInterpolationFilters=\"sRGB\"\n style={{ position: \"absolute\", width: 0, height: 0, overflow: \"hidden\" }}\n aria-hidden=\"true\"\n >\n <defs>\n <filter id={filterId}>\n <feGaussianBlur\n ref={blurRef}\n in=\"SourceGraphic\"\n stdDeviation={blur}\n result=\"blurred_source\"\n />\n <feImage\n href={maps.displacement}\n x=\"0\"\n y=\"0\"\n width={width}\n height={height}\n result=\"displacement_map\"\n />\n <feDisplacementMap\n ref={displacerRef}\n in=\"blurred_source\"\n in2=\"displacement_map\"\n scale={displacement}\n xChannelSelector=\"R\"\n yChannelSelector=\"G\"\n result=\"displaced\"\n />\n <feColorMatrix\n in=\"displaced\"\n type=\"saturate\"\n result=\"displaced_saturated\"\n values={saturation.toString()}\n />\n <feImage\n href={maps.specular}\n x=\"0\"\n y=\"0\"\n width={width}\n height={height}\n result=\"specular_layer\"\n />\n <feGaussianBlur\n in=\"specular_layer\"\n stdDeviation=\"1\"\n result=\"blurred_specular_layer\"\n />\n <feComposite\n in=\"displaced_saturated\"\n in2=\"blurred_specular_layer\"\n operator=\"in\"\n result=\"final_specular_layer\"\n />\n <feBlend\n in=\"final_specular_layer\"\n in2=\"displaced\"\n mode=\"normal\"\n />\n </filter>\n </defs>\n </svg>\n </>\n );\n }\n);\n","export function cn(...classes: (string | undefined | null | false)[]) {\n return classes.filter(Boolean).join(\" \");\n}\n","export interface MapOptions {\n width: number;\n height: number;\n radius?: number;\n edgeSize?: number;\n intensity?: number;\n specularWidth?: number;\n /** Supersampling multiplier for the displacement map (default: 2). Higher = smoother gradients. */\n quality?: number;\n}\n\nconst VERT = `attribute vec4 position; void main(){ gl_Position = position; }`;\n\nconst FRAG = `\nprecision highp float;\nuniform vec2 uRes;\nuniform float uRadius;\nuniform float uBorderSoftness;\nuniform float uSpecularWidth;\nuniform int uMode; // 0 = displacement, 1 = specular\n\nfloat sdRoundedBox(vec2 p, vec2 b, float r){\n r = min(r, min(b.x, b.y));\n vec2 q = abs(p) - b + r;\n return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;\n}\n\nvec3 calcNormal(vec2 p, vec2 b, float r){\n float e = max(0.5, min(b.x, b.y) * 0.01);\n vec2 h = vec2(e, 0.0);\n return normalize(vec3(\n sdRoundedBox(p+h.xy, b, r) - sdRoundedBox(p-h.xy, b, r),\n sdRoundedBox(p+h.yx, b, r) - sdRoundedBox(p-h.yx, b, r),\n -e * 2.0\n ));\n}\n\nvoid main(){\n vec2 p = gl_FragCoord.xy - uRes * 0.5;\n vec2 halfSize = uRes * 0.5 - 1.0;\n float d = sdRoundedBox(p, halfSize, uRadius);\n\n if(d > 0.0){ gl_FragColor = vec4(0.0); return; }\n\n if(uMode == 0){\n vec3 n = calcNormal(p, halfSize, uRadius);\n vec3 nc = n * 0.5 + 0.5;\n float border = smoothstep(-uBorderSoftness, 0.0, d);\n vec3 flat_ = vec3(0.5, 0.5, 1.0);\n gl_FragColor = vec4(mix(flat_, nc, border), 1.0);\n } else {\n float rim = smoothstep(-uSpecularWidth - 2.0, -uSpecularWidth, d)\n * (1.0 - smoothstep(-2.0, 0.0, d));\n float glow = smoothstep(-uBorderSoftness, 0.0, d) * 0.1;\n float s = clamp(rim + glow, 0.0, 1.0);\n gl_FragColor = vec4(vec3(s), s);\n }\n}\n`;\n\nfunction compile(gl: WebGLRenderingContext, type: number, src: string) {\n const s = gl.createShader(type)!;\n gl.shaderSource(s, src);\n gl.compileShader(s);\n return s;\n}\n\nlet _cachedProgram: { gl: WebGLRenderingContext; program: WebGLProgram; canvas: HTMLCanvasElement } | null = null;\n\nfunction getGL() {\n if (_cachedProgram) return _cachedProgram;\n\n const canvas = document.createElement(\"canvas\");\n const gl = canvas.getContext(\"webgl\", { preserveDrawingBuffer: true, premultipliedAlpha: false })!;\n\n const vs = compile(gl, gl.VERTEX_SHADER, VERT);\n const fs = compile(gl, gl.FRAGMENT_SHADER, FRAG);\n const program = gl.createProgram()!;\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n gl.useProgram(program);\n\n const buf = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, buf);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);\n const pos = gl.getAttribLocation(program, \"position\");\n gl.enableVertexAttribArray(pos);\n gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0);\n\n _cachedProgram = { gl, program, canvas };\n return _cachedProgram;\n}\n\nfunction render(\n width: number,\n height: number,\n radius: number,\n borderSoftness: number,\n specularWidth: number,\n mode: number,\n): string {\n const { gl, program, canvas } = getGL();\n canvas.width = width;\n canvas.height = height;\n gl.viewport(0, 0, width, height);\n gl.clearColor(0, 0, 0, 0);\n gl.clear(gl.COLOR_BUFFER_BIT);\n gl.useProgram(program);\n\n gl.uniform2f(gl.getUniformLocation(program, \"uRes\"), width, height);\n gl.uniform1f(gl.getUniformLocation(program, \"uRadius\"), radius);\n gl.uniform1f(gl.getUniformLocation(program, \"uBorderSoftness\"), borderSoftness);\n gl.uniform1f(gl.getUniformLocation(program, \"uSpecularWidth\"), specularWidth);\n gl.uniform1i(gl.getUniformLocation(program, \"uMode\"), mode);\n\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n return canvas.toDataURL(\"image/png\");\n}\n\nexport function generateGlassMaps(opts: MapOptions): {\n displacement: string;\n specular: string;\n} {\n const {\n width,\n height,\n radius = 60,\n edgeSize = 30,\n intensity = 0.7,\n specularWidth = 0.02,\n quality = 2,\n } = opts;\n\n const scale = Math.max(1, Math.round(quality));\n const rw = width * scale;\n const rh = height * scale;\n\n const r = Math.min(radius * scale, rw / 2, rh / 2);\n const borderSoftness = edgeSize * intensity * scale;\n const specPx = specularWidth * Math.min(rw, rh);\n\n return {\n displacement: render(rw, rh, r, borderSoftness, specPx, 0),\n specular: render(rw, rh, r, borderSoftness, specPx, 1),\n };\n}\n"],"mappings":"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","names":["LiquidGlassButton","gsap"]}
package/dist/index.d.cts CHANGED
@@ -33,6 +33,8 @@ interface LiquidGlassButtonProps extends React.ButtonHTMLAttributes<HTMLButtonEl
33
33
  hoverDuration?: number;
34
34
  /** Disable all GSAP animations */
35
35
  disableAnimation?: boolean;
36
+ /** Supersampling quality for the displacement map (default 2, higher = smoother) */
37
+ quality?: number;
36
38
  }
37
39
  declare const LiquidGlassButton: React.ForwardRefExoticComponent<LiquidGlassButtonProps & React.RefAttributes<HTMLButtonElement>>;
38
40
 
@@ -43,6 +45,8 @@ interface MapOptions {
43
45
  edgeSize?: number;
44
46
  intensity?: number;
45
47
  specularWidth?: number;
48
+ /** Supersampling multiplier for the displacement map (default: 2). Higher = smoother gradients. */
49
+ quality?: number;
46
50
  }
47
51
  declare function generateGlassMaps(opts: MapOptions): {
48
52
  displacement: string;
package/dist/index.d.ts CHANGED
@@ -33,6 +33,8 @@ interface LiquidGlassButtonProps extends React.ButtonHTMLAttributes<HTMLButtonEl
33
33
  hoverDuration?: number;
34
34
  /** Disable all GSAP animations */
35
35
  disableAnimation?: boolean;
36
+ /** Supersampling quality for the displacement map (default 2, higher = smoother) */
37
+ quality?: number;
36
38
  }
37
39
  declare const LiquidGlassButton: React.ForwardRefExoticComponent<LiquidGlassButtonProps & React.RefAttributes<HTMLButtonElement>>;
38
40
 
@@ -43,6 +45,8 @@ interface MapOptions {
43
45
  edgeSize?: number;
44
46
  intensity?: number;
45
47
  specularWidth?: number;
48
+ /** Supersampling multiplier for the displacement map (default: 2). Higher = smoother gradients. */
49
+ quality?: number;
46
50
  }
47
51
  declare function generateGlassMaps(opts: MapOptions): {
48
52
  displacement: string;
package/dist/index.js CHANGED
@@ -43,7 +43,7 @@ function cn(...classes) {
43
43
  // src/generate-displacement-map.ts
44
44
  var VERT = `attribute vec4 position; void main(){ gl_Position = position; }`;
45
45
  var FRAG = `
46
- precision mediump float;
46
+ precision highp float;
47
47
  uniform vec2 uRes;
48
48
  uniform float uRadius;
49
49
  uniform float uBorderSoftness;
@@ -57,7 +57,7 @@ float sdRoundedBox(vec2 p, vec2 b, float r){
57
57
  }
58
58
 
59
59
  vec3 calcNormal(vec2 p, vec2 b, float r){
60
- const float e = 1.0;
60
+ float e = max(0.5, min(b.x, b.y) * 0.01);
61
61
  vec2 h = vec2(e, 0.0);
62
62
  return normalize(vec3(
63
63
  sdRoundedBox(p+h.xy, b, r) - sdRoundedBox(p-h.xy, b, r),
@@ -138,14 +138,18 @@ function generateGlassMaps(opts) {
138
138
  radius = 60,
139
139
  edgeSize = 30,
140
140
  intensity = 0.7,
141
- specularWidth = 0.02
141
+ specularWidth = 0.02,
142
+ quality = 2
142
143
  } = opts;
143
- const r = Math.min(radius, width / 2, height / 2);
144
- const borderSoftness = edgeSize * intensity;
145
- const specPx = specularWidth * Math.min(width, height);
144
+ const scale = Math.max(1, Math.round(quality));
145
+ const rw = width * scale;
146
+ const rh = height * scale;
147
+ const r = Math.min(radius * scale, rw / 2, rh / 2);
148
+ const borderSoftness = edgeSize * intensity * scale;
149
+ const specPx = specularWidth * Math.min(rw, rh);
146
150
  return {
147
- displacement: render(width, height, r, borderSoftness, specPx, 0),
148
- specular: render(width, height, r, borderSoftness, specPx, 1)
151
+ displacement: render(rw, rh, r, borderSoftness, specPx, 0),
152
+ specular: render(rw, rh, r, borderSoftness, specPx, 1)
149
153
  };
150
154
  }
151
155
 
@@ -172,7 +176,8 @@ var LiquidGlassButton = forwardRef(
172
176
  hoverDisplacement = 125,
173
177
  hoverBlur = 4,
174
178
  hoverDuration = 0.25,
175
- disableAnimation = false
179
+ disableAnimation = false,
180
+ quality = 2
176
181
  } = _b, props = __objRest(_b, [
177
182
  "children",
178
183
  "className",
@@ -192,7 +197,8 @@ var LiquidGlassButton = forwardRef(
192
197
  "hoverDisplacement",
193
198
  "hoverBlur",
194
199
  "hoverDuration",
195
- "disableAnimation"
200
+ "disableAnimation",
201
+ "quality"
196
202
  ]);
197
203
  const internalRef = useRef(null);
198
204
  const buttonRef = ref != null ? ref : internalRef;
@@ -202,7 +208,7 @@ var LiquidGlassButton = forwardRef(
202
208
  const [maps, setMaps] = useState(null);
203
209
  useEffect(() => {
204
210
  let cancelled = false;
205
- const m = generateGlassMaps({ width, height, radius, edgeSize, intensity, specularWidth });
211
+ const m = generateGlassMaps({ width, height, radius, edgeSize, intensity, specularWidth, quality });
206
212
  let loaded = 0;
207
213
  const onLoad = () => {
208
214
  loaded++;
@@ -217,7 +223,7 @@ var LiquidGlassButton = forwardRef(
217
223
  return () => {
218
224
  cancelled = true;
219
225
  };
220
- }, [width, height, radius, edgeSize, intensity, specularWidth]);
226
+ }, [width, height, radius, edgeSize, intensity, specularWidth, quality]);
221
227
  useEffect(() => {
222
228
  const button = buttonRef.current;
223
229
  const displacer = displacerRef.current;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/liquid-glass-button.tsx","../src/utils.ts","../src/generate-displacement-map.ts"],"sourcesContent":["import React, { useRef, useEffect, useState, useId, forwardRef } from \"react\";\nimport gsap from \"gsap\";\nimport { cn } from \"./utils\";\nimport { generateGlassMaps } from \"./generate-displacement-map\";\n\nexport interface LiquidGlassButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Button width in px */\n width?: number;\n /** Button height in px */\n height?: number;\n /** Border radius in px */\n radius?: number;\n /** Edge thickness of the glass refraction zone */\n edgeSize?: number;\n /** Edge refraction intensity (0-1) */\n intensity?: number;\n /** Specular rim thickness relative to size (0-1) */\n specularWidth?: number;\n /** feDisplacementMap scale - how much the background refracts */\n displacement?: number;\n /** Gaussian blur applied to the background */\n blur?: number;\n /** Saturation applied to the displaced result */\n saturation?: number;\n /** Brightness boost on the backdrop-filter (1 = normal) */\n brightness?: number;\n /** Background tint color of the glass */\n glassColor?: string;\n /** Scale multiplier on hover */\n hoverScale?: number;\n /** Displacement scale on hover */\n hoverDisplacement?: number;\n /** Blur amount on hover */\n hoverBlur?: number;\n /** Duration of hover animation in seconds */\n hoverDuration?: number;\n /** Disable all GSAP animations */\n disableAnimation?: boolean;\n}\n\nexport const LiquidGlassButton = forwardRef<HTMLButtonElement, LiquidGlassButtonProps>(\n function LiquidGlassButton({\n children, className, style,\n width = 300,\n height = 56,\n radius = 60,\n edgeSize = 30,\n intensity = 0.7,\n specularWidth = 0.02,\n displacement = 55,\n blur = 1,\n saturation = 150,\n brightness = 1.1,\n glassColor = \"transparent\",\n hoverScale = 1.08,\n hoverDisplacement = 125,\n hoverBlur = 4,\n hoverDuration = 0.25,\n disableAnimation = false,\n ...props\n }, ref) {\n const internalRef = useRef<HTMLButtonElement>(null);\n const buttonRef = (ref as React.RefObject<HTMLButtonElement>) ?? internalRef;\n\n const displacerRef = useRef<SVGFEDisplacementMapElement>(null);\n const blurRef = useRef<SVGFEGaussianBlurElement>(null);\n\n const filterId = \"lg\" + useId().replace(/:/g, \"\");\n\n const [maps, setMaps] = useState<ReturnType<typeof generateGlassMaps> | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n const m = generateGlassMaps({ width, height, radius, edgeSize, intensity, specularWidth });\n\n // Pre-decode both PNGs\n let loaded = 0;\n const onLoad = () => {\n loaded++;\n if (loaded === 2 && !cancelled) setMaps(m);\n };\n\n const img1 = new Image();\n img1.onload = onLoad;\n img1.src = m.displacement;\n\n const img2 = new Image();\n img2.onload = onLoad;\n img2.src = m.specular;\n\n return () => { cancelled = true; };\n }, [width, height, radius, edgeSize, intensity, specularWidth]);\n\n useEffect(() => {\n const button = buttonRef.current;\n const displacer = displacerRef.current;\n const blurEl = blurRef.current;\n if (!button || !displacer || !blurEl || disableAnimation) return;\n\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n\n const fx = {\n displacement: displacement,\n blur: blur,\n };\n\n const sync = () => {\n displacer.setAttribute(\"scale\", fx.displacement.toString());\n blurEl.setAttribute(\"stdDeviation\", fx.blur.toString());\n };\n\n sync();\n\n const onEnter = () => {\n gsap.killTweensOf([fx, button]);\n gsap.to(fx, {\n displacement: hoverDisplacement,\n blur: hoverBlur,\n duration: hoverDuration,\n ease: \"back.out(1.4)\",\n onUpdate: sync,\n });\n gsap.to(button, {\n scale: hoverScale,\n duration: hoverDuration,\n ease: \"back.out(1.4)\",\n });\n };\n\n const onLeave = () => {\n gsap.killTweensOf([fx, button]);\n gsap.to(fx, {\n displacement: displacement,\n blur: blur,\n duration: hoverDuration,\n ease: \"power2.out\",\n onUpdate: sync,\n });\n gsap.to(button, {\n scale: 1,\n duration: hoverDuration,\n ease: \"power2.out\",\n });\n };\n\n const onClick = () => {\n gsap.killTweensOf(button);\n const cur = gsap.getProperty(button, \"scale\") as number;\n gsap.timeline()\n .to(button, { scale: cur * 0.92, duration: 0.08, ease: \"power2.in\" })\n .to(button, { scale: hoverScale, duration: 0.25, ease: \"back.out(2)\" });\n };\n\n button.addEventListener(\"pointerenter\", onEnter);\n button.addEventListener(\"pointerleave\", onLeave);\n button.addEventListener(\"click\", onClick);\n\n return () => {\n button.removeEventListener(\"pointerenter\", onEnter);\n button.removeEventListener(\"pointerleave\", onLeave);\n button.removeEventListener(\"click\", onClick);\n gsap.killTweensOf([button, fx]);\n };\n }, [buttonRef, maps, displacement, blur, hoverScale, hoverDisplacement, hoverBlur, hoverDuration, disableAnimation]);\n\n if (!maps) return null;\n\n return (\n <>\n <button\n ref={buttonRef}\n className={cn(\"relative overflow-hidden shadow-lg cursor-pointer\", className)}\n style={{ width, height, borderRadius: radius, border: \"none\", background: glassColor, ...style }}\n {...props}\n >\n <div\n className=\"absolute inset-0\"\n style={{\n backdropFilter: `url(#${filterId}) brightness(${brightness * 100}%)`,\n WebkitBackdropFilter: `url(#${filterId}) brightness(${brightness * 100}%)`,\n borderRadius: \"inherit\",\n willChange: \"backdrop-filter\",\n transform: \"translateZ(0)\",\n }}\n />\n <div\n className=\"absolute inset-0 inline-flex items-center justify-center font-bold text-white\"\n style={{ background: \"hsl(0 100% 100% / 15%)\", borderRadius: \"inherit\" }}\n >\n {children}\n </div>\n </button>\n\n <svg\n colorInterpolationFilters=\"sRGB\"\n style={{ position: \"absolute\", width: 0, height: 0, overflow: \"hidden\" }}\n aria-hidden=\"true\"\n >\n <defs>\n <filter id={filterId}>\n <feGaussianBlur\n ref={blurRef}\n in=\"SourceGraphic\"\n stdDeviation={blur}\n result=\"blurred_source\"\n />\n <feImage\n href={maps.displacement}\n x=\"0\"\n y=\"0\"\n width={width}\n height={height}\n result=\"displacement_map\"\n />\n <feDisplacementMap\n ref={displacerRef}\n in=\"blurred_source\"\n in2=\"displacement_map\"\n scale={displacement}\n xChannelSelector=\"R\"\n yChannelSelector=\"G\"\n result=\"displaced\"\n />\n <feColorMatrix\n in=\"displaced\"\n type=\"saturate\"\n result=\"displaced_saturated\"\n values={saturation.toString()}\n />\n <feImage\n href={maps.specular}\n x=\"0\"\n y=\"0\"\n width={width}\n height={height}\n result=\"specular_layer\"\n />\n <feGaussianBlur\n in=\"specular_layer\"\n stdDeviation=\"1\"\n result=\"blurred_specular_layer\"\n />\n <feComposite\n in=\"displaced_saturated\"\n in2=\"blurred_specular_layer\"\n operator=\"in\"\n result=\"final_specular_layer\"\n />\n <feBlend\n in=\"final_specular_layer\"\n in2=\"displaced\"\n mode=\"normal\"\n />\n </filter>\n </defs>\n </svg>\n </>\n );\n }\n);\n","export function cn(...classes: (string | undefined | null | false)[]) {\n return classes.filter(Boolean).join(\" \");\n}\n","export interface MapOptions {\n width: number;\n height: number;\n radius?: number;\n edgeSize?: number;\n intensity?: number;\n specularWidth?: number;\n}\n\nconst VERT = `attribute vec4 position; void main(){ gl_Position = position; }`;\n\nconst FRAG = `\nprecision mediump float;\nuniform vec2 uRes;\nuniform float uRadius;\nuniform float uBorderSoftness;\nuniform float uSpecularWidth;\nuniform int uMode; // 0 = displacement, 1 = specular\n\nfloat sdRoundedBox(vec2 p, vec2 b, float r){\n r = min(r, min(b.x, b.y));\n vec2 q = abs(p) - b + r;\n return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;\n}\n\nvec3 calcNormal(vec2 p, vec2 b, float r){\n const float e = 1.0;\n vec2 h = vec2(e, 0.0);\n return normalize(vec3(\n sdRoundedBox(p+h.xy, b, r) - sdRoundedBox(p-h.xy, b, r),\n sdRoundedBox(p+h.yx, b, r) - sdRoundedBox(p-h.yx, b, r),\n -e * 2.0\n ));\n}\n\nvoid main(){\n vec2 p = gl_FragCoord.xy - uRes * 0.5;\n vec2 halfSize = uRes * 0.5 - 1.0;\n float d = sdRoundedBox(p, halfSize, uRadius);\n\n if(d > 0.0){ gl_FragColor = vec4(0.0); return; }\n\n if(uMode == 0){\n vec3 n = calcNormal(p, halfSize, uRadius);\n vec3 nc = n * 0.5 + 0.5;\n float border = smoothstep(-uBorderSoftness, 0.0, d);\n vec3 flat_ = vec3(0.5, 0.5, 1.0);\n gl_FragColor = vec4(mix(flat_, nc, border), 1.0);\n } else {\n float rim = smoothstep(-uSpecularWidth - 2.0, -uSpecularWidth, d)\n * (1.0 - smoothstep(-2.0, 0.0, d));\n float glow = smoothstep(-uBorderSoftness, 0.0, d) * 0.1;\n float s = clamp(rim + glow, 0.0, 1.0);\n gl_FragColor = vec4(vec3(s), s);\n }\n}\n`;\n\nfunction compile(gl: WebGLRenderingContext, type: number, src: string) {\n const s = gl.createShader(type)!;\n gl.shaderSource(s, src);\n gl.compileShader(s);\n return s;\n}\n\nlet _cachedProgram: { gl: WebGLRenderingContext; program: WebGLProgram; canvas: HTMLCanvasElement } | null = null;\n\nfunction getGL() {\n if (_cachedProgram) return _cachedProgram;\n\n const canvas = document.createElement(\"canvas\");\n const gl = canvas.getContext(\"webgl\", { preserveDrawingBuffer: true, premultipliedAlpha: false })!;\n\n const vs = compile(gl, gl.VERTEX_SHADER, VERT);\n const fs = compile(gl, gl.FRAGMENT_SHADER, FRAG);\n const program = gl.createProgram()!;\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n gl.useProgram(program);\n\n const buf = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, buf);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);\n const pos = gl.getAttribLocation(program, \"position\");\n gl.enableVertexAttribArray(pos);\n gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0);\n\n _cachedProgram = { gl, program, canvas };\n return _cachedProgram;\n}\n\nfunction render(\n width: number,\n height: number,\n radius: number,\n borderSoftness: number,\n specularWidth: number,\n mode: number,\n): string {\n const { gl, program, canvas } = getGL();\n canvas.width = width;\n canvas.height = height;\n gl.viewport(0, 0, width, height);\n gl.clearColor(0, 0, 0, 0);\n gl.clear(gl.COLOR_BUFFER_BIT);\n gl.useProgram(program);\n\n gl.uniform2f(gl.getUniformLocation(program, \"uRes\"), width, height);\n gl.uniform1f(gl.getUniformLocation(program, \"uRadius\"), radius);\n gl.uniform1f(gl.getUniformLocation(program, \"uBorderSoftness\"), borderSoftness);\n gl.uniform1f(gl.getUniformLocation(program, \"uSpecularWidth\"), specularWidth);\n gl.uniform1i(gl.getUniformLocation(program, \"uMode\"), mode);\n\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n return canvas.toDataURL(\"image/png\");\n}\n\nexport function generateGlassMaps(opts: MapOptions): {\n displacement: string;\n specular: string;\n} {\n const {\n width,\n height,\n radius = 60,\n edgeSize = 30,\n intensity = 0.7,\n specularWidth = 0.02,\n } = opts;\n\n const r = Math.min(radius, width / 2, height / 2);\n const borderSoftness = edgeSize * intensity;\n const specPx = specularWidth * Math.min(width, height);\n\n return {\n displacement: render(width, height, r, borderSoftness, specPx, 0),\n specular: render(width, height, r, borderSoftness, specPx, 1),\n };\n}\n"],"mappings":"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","names":["LiquidGlassButton"]}
1
+ {"version":3,"sources":["../src/liquid-glass-button.tsx","../src/utils.ts","../src/generate-displacement-map.ts"],"sourcesContent":["import React, { useRef, useEffect, useState, useId, forwardRef } from \"react\";\nimport gsap from \"gsap\";\nimport { cn } from \"./utils\";\nimport { generateGlassMaps } from \"./generate-displacement-map\";\n\nexport interface LiquidGlassButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n /** Button width in px */\n width?: number;\n /** Button height in px */\n height?: number;\n /** Border radius in px */\n radius?: number;\n /** Edge thickness of the glass refraction zone */\n edgeSize?: number;\n /** Edge refraction intensity (0-1) */\n intensity?: number;\n /** Specular rim thickness relative to size (0-1) */\n specularWidth?: number;\n /** feDisplacementMap scale - how much the background refracts */\n displacement?: number;\n /** Gaussian blur applied to the background */\n blur?: number;\n /** Saturation applied to the displaced result */\n saturation?: number;\n /** Brightness boost on the backdrop-filter (1 = normal) */\n brightness?: number;\n /** Background tint color of the glass */\n glassColor?: string;\n /** Scale multiplier on hover */\n hoverScale?: number;\n /** Displacement scale on hover */\n hoverDisplacement?: number;\n /** Blur amount on hover */\n hoverBlur?: number;\n /** Duration of hover animation in seconds */\n hoverDuration?: number;\n /** Disable all GSAP animations */\n disableAnimation?: boolean;\n /** Supersampling quality for the displacement map (default 2, higher = smoother) */\n quality?: number;\n}\n\nexport const LiquidGlassButton = forwardRef<HTMLButtonElement, LiquidGlassButtonProps>(\n function LiquidGlassButton({\n children, className, style,\n width = 300,\n height = 56,\n radius = 60,\n edgeSize = 30,\n intensity = 0.7,\n specularWidth = 0.02,\n displacement = 55,\n blur = 1,\n saturation = 150,\n brightness = 1.1,\n glassColor = \"transparent\",\n hoverScale = 1.08,\n hoverDisplacement = 125,\n hoverBlur = 4,\n hoverDuration = 0.25,\n disableAnimation = false,\n quality = 2,\n ...props\n }, ref) {\n const internalRef = useRef<HTMLButtonElement>(null);\n const buttonRef = (ref as React.RefObject<HTMLButtonElement>) ?? internalRef;\n\n const displacerRef = useRef<SVGFEDisplacementMapElement>(null);\n const blurRef = useRef<SVGFEGaussianBlurElement>(null);\n\n const filterId = \"lg\" + useId().replace(/:/g, \"\");\n\n const [maps, setMaps] = useState<ReturnType<typeof generateGlassMaps> | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n const m = generateGlassMaps({ width, height, radius, edgeSize, intensity, specularWidth, quality });\n\n // Pre-decode both PNGs\n let loaded = 0;\n const onLoad = () => {\n loaded++;\n if (loaded === 2 && !cancelled) setMaps(m);\n };\n\n const img1 = new Image();\n img1.onload = onLoad;\n img1.src = m.displacement;\n\n const img2 = new Image();\n img2.onload = onLoad;\n img2.src = m.specular;\n\n return () => { cancelled = true; };\n }, [width, height, radius, edgeSize, intensity, specularWidth, quality]);\n\n useEffect(() => {\n const button = buttonRef.current;\n const displacer = displacerRef.current;\n const blurEl = blurRef.current;\n if (!button || !displacer || !blurEl || disableAnimation) return;\n\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n\n const fx = {\n displacement: displacement,\n blur: blur,\n };\n\n const sync = () => {\n displacer.setAttribute(\"scale\", fx.displacement.toString());\n blurEl.setAttribute(\"stdDeviation\", fx.blur.toString());\n };\n\n sync();\n\n const onEnter = () => {\n gsap.killTweensOf([fx, button]);\n gsap.to(fx, {\n displacement: hoverDisplacement,\n blur: hoverBlur,\n duration: hoverDuration,\n ease: \"back.out(1.4)\",\n onUpdate: sync,\n });\n gsap.to(button, {\n scale: hoverScale,\n duration: hoverDuration,\n ease: \"back.out(1.4)\",\n });\n };\n\n const onLeave = () => {\n gsap.killTweensOf([fx, button]);\n gsap.to(fx, {\n displacement: displacement,\n blur: blur,\n duration: hoverDuration,\n ease: \"power2.out\",\n onUpdate: sync,\n });\n gsap.to(button, {\n scale: 1,\n duration: hoverDuration,\n ease: \"power2.out\",\n });\n };\n\n const onClick = () => {\n gsap.killTweensOf(button);\n const cur = gsap.getProperty(button, \"scale\") as number;\n gsap.timeline()\n .to(button, { scale: cur * 0.92, duration: 0.08, ease: \"power2.in\" })\n .to(button, { scale: hoverScale, duration: 0.25, ease: \"back.out(2)\" });\n };\n\n button.addEventListener(\"pointerenter\", onEnter);\n button.addEventListener(\"pointerleave\", onLeave);\n button.addEventListener(\"click\", onClick);\n\n return () => {\n button.removeEventListener(\"pointerenter\", onEnter);\n button.removeEventListener(\"pointerleave\", onLeave);\n button.removeEventListener(\"click\", onClick);\n gsap.killTweensOf([button, fx]);\n };\n }, [buttonRef, maps, displacement, blur, hoverScale, hoverDisplacement, hoverBlur, hoverDuration, disableAnimation]);\n\n if (!maps) return null;\n\n return (\n <>\n <button\n ref={buttonRef}\n className={cn(\"relative overflow-hidden shadow-lg cursor-pointer\", className)}\n style={{ width, height, borderRadius: radius, border: \"none\", background: glassColor, ...style }}\n {...props}\n >\n <div\n className=\"absolute inset-0\"\n style={{\n backdropFilter: `url(#${filterId}) brightness(${brightness * 100}%)`,\n WebkitBackdropFilter: `url(#${filterId}) brightness(${brightness * 100}%)`,\n borderRadius: \"inherit\",\n willChange: \"backdrop-filter\",\n transform: \"translateZ(0)\",\n }}\n />\n <div\n className=\"absolute inset-0 inline-flex items-center justify-center font-bold text-white\"\n style={{ background: \"hsl(0 100% 100% / 15%)\", borderRadius: \"inherit\" }}\n >\n {children}\n </div>\n </button>\n\n <svg\n colorInterpolationFilters=\"sRGB\"\n style={{ position: \"absolute\", width: 0, height: 0, overflow: \"hidden\" }}\n aria-hidden=\"true\"\n >\n <defs>\n <filter id={filterId}>\n <feGaussianBlur\n ref={blurRef}\n in=\"SourceGraphic\"\n stdDeviation={blur}\n result=\"blurred_source\"\n />\n <feImage\n href={maps.displacement}\n x=\"0\"\n y=\"0\"\n width={width}\n height={height}\n result=\"displacement_map\"\n />\n <feDisplacementMap\n ref={displacerRef}\n in=\"blurred_source\"\n in2=\"displacement_map\"\n scale={displacement}\n xChannelSelector=\"R\"\n yChannelSelector=\"G\"\n result=\"displaced\"\n />\n <feColorMatrix\n in=\"displaced\"\n type=\"saturate\"\n result=\"displaced_saturated\"\n values={saturation.toString()}\n />\n <feImage\n href={maps.specular}\n x=\"0\"\n y=\"0\"\n width={width}\n height={height}\n result=\"specular_layer\"\n />\n <feGaussianBlur\n in=\"specular_layer\"\n stdDeviation=\"1\"\n result=\"blurred_specular_layer\"\n />\n <feComposite\n in=\"displaced_saturated\"\n in2=\"blurred_specular_layer\"\n operator=\"in\"\n result=\"final_specular_layer\"\n />\n <feBlend\n in=\"final_specular_layer\"\n in2=\"displaced\"\n mode=\"normal\"\n />\n </filter>\n </defs>\n </svg>\n </>\n );\n }\n);\n","export function cn(...classes: (string | undefined | null | false)[]) {\n return classes.filter(Boolean).join(\" \");\n}\n","export interface MapOptions {\n width: number;\n height: number;\n radius?: number;\n edgeSize?: number;\n intensity?: number;\n specularWidth?: number;\n /** Supersampling multiplier for the displacement map (default: 2). Higher = smoother gradients. */\n quality?: number;\n}\n\nconst VERT = `attribute vec4 position; void main(){ gl_Position = position; }`;\n\nconst FRAG = `\nprecision highp float;\nuniform vec2 uRes;\nuniform float uRadius;\nuniform float uBorderSoftness;\nuniform float uSpecularWidth;\nuniform int uMode; // 0 = displacement, 1 = specular\n\nfloat sdRoundedBox(vec2 p, vec2 b, float r){\n r = min(r, min(b.x, b.y));\n vec2 q = abs(p) - b + r;\n return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;\n}\n\nvec3 calcNormal(vec2 p, vec2 b, float r){\n float e = max(0.5, min(b.x, b.y) * 0.01);\n vec2 h = vec2(e, 0.0);\n return normalize(vec3(\n sdRoundedBox(p+h.xy, b, r) - sdRoundedBox(p-h.xy, b, r),\n sdRoundedBox(p+h.yx, b, r) - sdRoundedBox(p-h.yx, b, r),\n -e * 2.0\n ));\n}\n\nvoid main(){\n vec2 p = gl_FragCoord.xy - uRes * 0.5;\n vec2 halfSize = uRes * 0.5 - 1.0;\n float d = sdRoundedBox(p, halfSize, uRadius);\n\n if(d > 0.0){ gl_FragColor = vec4(0.0); return; }\n\n if(uMode == 0){\n vec3 n = calcNormal(p, halfSize, uRadius);\n vec3 nc = n * 0.5 + 0.5;\n float border = smoothstep(-uBorderSoftness, 0.0, d);\n vec3 flat_ = vec3(0.5, 0.5, 1.0);\n gl_FragColor = vec4(mix(flat_, nc, border), 1.0);\n } else {\n float rim = smoothstep(-uSpecularWidth - 2.0, -uSpecularWidth, d)\n * (1.0 - smoothstep(-2.0, 0.0, d));\n float glow = smoothstep(-uBorderSoftness, 0.0, d) * 0.1;\n float s = clamp(rim + glow, 0.0, 1.0);\n gl_FragColor = vec4(vec3(s), s);\n }\n}\n`;\n\nfunction compile(gl: WebGLRenderingContext, type: number, src: string) {\n const s = gl.createShader(type)!;\n gl.shaderSource(s, src);\n gl.compileShader(s);\n return s;\n}\n\nlet _cachedProgram: { gl: WebGLRenderingContext; program: WebGLProgram; canvas: HTMLCanvasElement } | null = null;\n\nfunction getGL() {\n if (_cachedProgram) return _cachedProgram;\n\n const canvas = document.createElement(\"canvas\");\n const gl = canvas.getContext(\"webgl\", { preserveDrawingBuffer: true, premultipliedAlpha: false })!;\n\n const vs = compile(gl, gl.VERTEX_SHADER, VERT);\n const fs = compile(gl, gl.FRAGMENT_SHADER, FRAG);\n const program = gl.createProgram()!;\n gl.attachShader(program, vs);\n gl.attachShader(program, fs);\n gl.linkProgram(program);\n gl.useProgram(program);\n\n const buf = gl.createBuffer();\n gl.bindBuffer(gl.ARRAY_BUFFER, buf);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);\n const pos = gl.getAttribLocation(program, \"position\");\n gl.enableVertexAttribArray(pos);\n gl.vertexAttribPointer(pos, 2, gl.FLOAT, false, 0, 0);\n\n _cachedProgram = { gl, program, canvas };\n return _cachedProgram;\n}\n\nfunction render(\n width: number,\n height: number,\n radius: number,\n borderSoftness: number,\n specularWidth: number,\n mode: number,\n): string {\n const { gl, program, canvas } = getGL();\n canvas.width = width;\n canvas.height = height;\n gl.viewport(0, 0, width, height);\n gl.clearColor(0, 0, 0, 0);\n gl.clear(gl.COLOR_BUFFER_BIT);\n gl.useProgram(program);\n\n gl.uniform2f(gl.getUniformLocation(program, \"uRes\"), width, height);\n gl.uniform1f(gl.getUniformLocation(program, \"uRadius\"), radius);\n gl.uniform1f(gl.getUniformLocation(program, \"uBorderSoftness\"), borderSoftness);\n gl.uniform1f(gl.getUniformLocation(program, \"uSpecularWidth\"), specularWidth);\n gl.uniform1i(gl.getUniformLocation(program, \"uMode\"), mode);\n\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n return canvas.toDataURL(\"image/png\");\n}\n\nexport function generateGlassMaps(opts: MapOptions): {\n displacement: string;\n specular: string;\n} {\n const {\n width,\n height,\n radius = 60,\n edgeSize = 30,\n intensity = 0.7,\n specularWidth = 0.02,\n quality = 2,\n } = opts;\n\n const scale = Math.max(1, Math.round(quality));\n const rw = width * scale;\n const rh = height * scale;\n\n const r = Math.min(radius * scale, rw / 2, rh / 2);\n const borderSoftness = edgeSize * intensity * scale;\n const specPx = specularWidth * Math.min(rw, rh);\n\n return {\n displacement: render(rw, rh, r, borderSoftness, specPx, 0),\n specular: render(rw, rh, r, borderSoftness, specPx, 1),\n };\n}\n"],"mappings":"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","names":["LiquidGlassButton"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marcosdemik/liquidglass",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Liquid Glass UI effect for React - glassmorphism refraction with SVG filters, WebGL and GSAP animations",
5
5
  "author": "Marcos Demik <marcosdemik>",
6
6
  "license": "MIT",