glasskit-js 1.0.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LiquidGlass.d.ts +3 -1
- package/LiquidGlass.mjs +12 -3
- package/README.md +5 -1
- package/liquid-glass.d.ts +2 -2
- package/liquid-glass.js +21 -6
- package/package.json +1 -1
package/LiquidGlass.d.ts
CHANGED
|
@@ -9,5 +9,7 @@ export interface LiquidGlassProps
|
|
|
9
9
|
children?: React.ReactNode;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
declare const LiquidGlass: React.
|
|
12
|
+
declare const LiquidGlass: React.ForwardRefExoticComponent<
|
|
13
|
+
LiquidGlassProps & React.RefAttributes<HTMLElement>
|
|
14
|
+
>;
|
|
13
15
|
export default LiquidGlass;
|
package/LiquidGlass.mjs
CHANGED
|
@@ -22,7 +22,7 @@ const GLASS_KEYS = [
|
|
|
22
22
|
"tint", "tintOpacity", "sheen", "sheenColor", "saturate", "brightness", "shadow", "radius", "background",
|
|
23
23
|
];
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
const LiquidGlass = React.forwardRef(function LiquidGlass({ as: Tag = "div", children, ...props }, forwardedRef) {
|
|
26
26
|
const ref = useRef(null);
|
|
27
27
|
const instRef = useRef(null);
|
|
28
28
|
|
|
@@ -47,5 +47,14 @@ export default function LiquidGlass({ as: Tag = "div", children, ...props }) {
|
|
|
47
47
|
instRef.current?.update(JSON.parse(optsKey));
|
|
48
48
|
}, [optsKey]);
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
// keep our internal ref and forward the node to the caller's ref too
|
|
51
|
+
const setRef = (node) => {
|
|
52
|
+
ref.current = node;
|
|
53
|
+
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
54
|
+
else if (forwardedRef) forwardedRef.current = node;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return React.createElement(Tag, { ref: setRef, ...rest }, children);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export default LiquidGlass;
|
package/README.md
CHANGED
|
@@ -73,7 +73,11 @@ It refracts DOM, not `<canvas>` pixels — use `webgl` for canvas/video backgrou
|
|
|
73
73
|
| Depth | `depth` | | tint | `tint`, `tintOpacity` |
|
|
74
74
|
| Dispersion | `dispersion` | | corner radius | `radius` |
|
|
75
75
|
| Splay | `splay` | | | |
|
|
76
|
-
| Light (angle / %) | `lightAngle` / `lightIntensity` | | | |
|
|
76
|
+
| Light (angle / %) | `lightAngle` / `lightIntensity` | | drop shadow | `shadow` |
|
|
77
|
+
|
|
78
|
+
- **`tint`** takes either `"r,g,b"` (paired with `tintOpacity`) **or a full CSS gradient** — `tint="linear-gradient(180deg, rgba(255,255,255,.2), rgba(19,19,19,.22))"`.
|
|
79
|
+
- **`radius`** sets `border-radius` on the element *and* the refraction map — `radius={999}` alone gives you a pill; no need to also set it in `style`.
|
|
80
|
+
- **`shadow`** is any CSS `box-shadow` (`"none"` removes it); the inner light border/bezel follow `lightIntensity`.
|
|
77
81
|
|
|
78
82
|
## Shapes
|
|
79
83
|
|
package/liquid-glass.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface LiquidGlassOptions {
|
|
|
31
31
|
curvature?: number;
|
|
32
32
|
/** +1 convex (magnify) .. 0 flat .. -1 concave (shrink). @default 1 */
|
|
33
33
|
convexity?: number;
|
|
34
|
-
/** Glass tint
|
|
34
|
+
/** Glass tint — either "r,g,b" (combined with `tintOpacity`) or a full CSS `gradient(...)` string (opacity baked into its color stops). @default "255,255,255" */
|
|
35
35
|
tint?: string;
|
|
36
36
|
/** Tint opacity 0..1. @default 0.08 */
|
|
37
37
|
tintOpacity?: number;
|
|
@@ -45,7 +45,7 @@ export interface LiquidGlassOptions {
|
|
|
45
45
|
brightness?: number;
|
|
46
46
|
/** Outer drop shadow as any CSS box-shadow value; "none" or "" removes it. The inner light border/bezel are controlled by `lightIntensity`. @default "0 8px 30px rgba(0,0,0,0.18)" */
|
|
47
47
|
shadow?: string;
|
|
48
|
-
/**
|
|
48
|
+
/** Corner radius in px. When set, it both feeds the refraction map AND applies `border-radius` to the element. null = read the element's existing border-radius. @default null */
|
|
49
49
|
radius?: number | null;
|
|
50
50
|
/** Element or selector to refract. Required for `svg-clone` and `webgl`. */
|
|
51
51
|
background?: Element | string | null;
|
package/liquid-glass.js
CHANGED
|
@@ -64,7 +64,19 @@
|
|
|
64
64
|
if (typeof v !== 'string' || !v || v === 'none') return '';
|
|
65
65
|
return /url\(|expression|javascript:|[;{}<>]/i.test(v) ? '' : v;
|
|
66
66
|
}
|
|
67
|
+
// allow CSS gradient values only; block url()/image-set()/extra declarations
|
|
68
|
+
function safeGradient(v) {
|
|
69
|
+
if (typeof v !== 'string') return '';
|
|
70
|
+
if (/url\(|image-set|element\(|expression|javascript:|[;{}<>]/i.test(v)) return '';
|
|
71
|
+
return /(^|\s)(repeating-)?(linear|radial|conic)-gradient\(/i.test(v) ? v : '';
|
|
72
|
+
}
|
|
67
73
|
function num(v, d) { var n = parseFloat(v); return isFinite(n) ? n : d; }
|
|
74
|
+
// tint background: a full CSS gradient string, else an rgba() built from "r,g,b" + opacity
|
|
75
|
+
function tintValue(o) {
|
|
76
|
+
var t = o.tint;
|
|
77
|
+
if (typeof t === 'string' && /gradient\(/i.test(t)) { var g = safeGradient(t); if (g) return g; }
|
|
78
|
+
return 'rgba(' + safeRGB(t) + ',' + num(o.tintOpacity, 0.08) + ')';
|
|
79
|
+
}
|
|
68
80
|
function resolveEl(x) { return typeof x === 'string' ? document.querySelector(x) : x; }
|
|
69
81
|
function readRadius(el) { return parseFloat(getComputedStyle(el).borderRadius) || 0; }
|
|
70
82
|
function ns(tag) { return document.createElementNS('http://www.w3.org/2000/svg', tag); }
|
|
@@ -117,7 +129,7 @@
|
|
|
117
129
|
if ((mode === 'svg-clone' || mode === 'webgl') && !bg) mode = isChromium() ? 'svg' : 'css';
|
|
118
130
|
|
|
119
131
|
if (getComputedStyle(el).position === 'static') el.style.position = 'relative';
|
|
120
|
-
var prevBg = el.style.
|
|
132
|
+
var prevBg = el.style.background, prevRadius = el.style.borderRadius;
|
|
121
133
|
|
|
122
134
|
var overlay = document.createElement('div');
|
|
123
135
|
overlay.style.cssText = 'position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:2;';
|
|
@@ -251,7 +263,7 @@
|
|
|
251
263
|
/* ---- tint + specular overlay (all modes) ---- */
|
|
252
264
|
function applyOverlay() {
|
|
253
265
|
var li = num(o.lightIntensity, 0.8), a = (num(o.lightAngle, -45) + 90) * Math.PI / 180;
|
|
254
|
-
if (mode === 'css' || mode === 'svg') el.style.
|
|
266
|
+
if (mode === 'css' || mode === 'svg') el.style.background = tintValue(o);
|
|
255
267
|
// FACE gloss — controlled by `sheen`/`sheenColor`, independent of the border light below
|
|
256
268
|
var sc = safeRGB(o.sheenColor), sh = num(o.sheen, 0.7);
|
|
257
269
|
overlay.style.background = sh <= 0 ? 'none' :
|
|
@@ -371,14 +383,17 @@
|
|
|
371
383
|
var ro = new ResizeObserver(function () { refreshMap(); if (mode === 'svg-clone') syncClone(); });
|
|
372
384
|
ro.observe(el);
|
|
373
385
|
|
|
374
|
-
|
|
386
|
+
// when an explicit radius is given, round the element itself (not just the refraction map)
|
|
387
|
+
function applyRadius() { if (o.radius != null) el.style.borderRadius = num(o.radius, 0) + 'px'; }
|
|
388
|
+
|
|
389
|
+
refreshMap(); applyFilterParams(); applyOverlay(); applyRadius();
|
|
375
390
|
|
|
376
391
|
var instance = {
|
|
377
392
|
el: el, mode: mode,
|
|
378
393
|
update: function (patch) {
|
|
379
394
|
Object.assign(o, patch || {});
|
|
380
395
|
if (patch && patch.background != null && mode === 'webgl') loadGLBg(resolveEl(o.background));
|
|
381
|
-
refreshMap(); applyFilterParams(); applyOverlay();
|
|
396
|
+
refreshMap(); applyFilterParams(); applyOverlay(); applyRadius();
|
|
382
397
|
return instance;
|
|
383
398
|
},
|
|
384
399
|
refresh: function () { if (mode === 'svg-clone') reclone(); else if (mode === 'webgl') loadGLBg(resolveEl(o.background)); return instance; },
|
|
@@ -388,7 +403,7 @@
|
|
|
388
403
|
// free the WebGL context explicitly — browsers cap ~16 per page
|
|
389
404
|
if (gl) { var lc = gl.getExtension('WEBGL_lose_context'); if (lc) lc.loseContext(); gl = null; }
|
|
390
405
|
[svg, lensWrap, glcanvas, overlay].forEach(function (n) { if (n && n.parentNode) n.parentNode.removeChild(n); });
|
|
391
|
-
el.style.backdropFilter = ''; el.style.webkitBackdropFilter = ''; el.style.
|
|
406
|
+
el.style.backdropFilter = ''; el.style.webkitBackdropFilter = ''; el.style.background = prevBg; el.style.borderRadius = prevRadius;
|
|
392
407
|
}
|
|
393
408
|
};
|
|
394
409
|
return instance;
|
|
@@ -424,5 +439,5 @@
|
|
|
424
439
|
}
|
|
425
440
|
if (typeof window !== 'undefined') { if (document.readyState !== 'loading') defineElement(); else document.addEventListener('DOMContentLoaded', defineElement); }
|
|
426
441
|
|
|
427
|
-
return { apply: apply, defineElement: defineElement, isChromium: isChromium, pickMode: pickMode, DEFAULTS: DEFAULTS, version: '1.0
|
|
442
|
+
return { apply: apply, defineElement: defineElement, isChromium: isChromium, pickMode: pickMode, DEFAULTS: DEFAULTS, version: '1.1.0' };
|
|
428
443
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glasskit-js",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Drop-in Apple/Figma 'Liquid Glass' for any element — switch between pure CSS, SVG displacement (live backdrop), cross-browser clone-mode, or WebGL. Real refraction, chromatic aberration, specular highlight. Zero dependencies. Class + <liquid-glass> web component.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"liquid-glass",
|